<template>
  <div class="log-viewer">
    <!-- 页面提示区域 -->
    <div v-if="pageNotification.show" class="page-notification" :class="pageNotification.type">
      {{ pageNotification.message }}
    </div>
    <div class="main-layout">
      <!-- 左侧：日志类型选择区域 -->
      <div class="left-panel">
        <n-card size="small" class="log-type-card" title=" ">
          <template #header-extra>
            <div class="card-header-icons">
              <div class="header-icon mdb-icon">
                <n-button
                  quaternary
                  circle
                  size="medium"
                  @click="openMdbDrawer('BMC')"
                  class="mdb-btn component-mdb-btn csr-btn"
                  title="查看CSR加载信息"
                >
                  <template #icon>
                    <span class="mdb-text">CSR</span>
                  </template>
                </n-button>
              </div>
              <!-- CSR和BMC按钮之间的分割线 -->
              <div class="csr-bmc-separator"></div>
              <div class="header-icon bmc-icon" @click="openBmcVersionDrawer" style="cursor: pointer;">
                <n-icon size="16">
                  <InformationCircleOutline />
                </n-icon>
                <span>BMC版本</span>
              </div>
              <div class="header-icon app-icon" @click="openAppVersionDrawer" style="cursor: pointer;">
                <n-icon size="16">
                  <LibraryOutline />
                </n-icon>
                <span>组件版本</span>
              </div>
            </div>
          </template>

          <div class="log-type-switches">
            <!-- BMC版本和组件版本与操作日志之间的分割线 -->
            <div class="version-separator"></div>
            
            <div class="log-type-section" :class="{ 
              'has-filtered-data': areaFilterStatus.operation === true && (debouncedSearchText || (startTime && endTime)),
              'no-filtered-data': areaFilterStatus.operation === false && (debouncedSearchText || (startTime && endTime))
            }">
              <div class="log-type-title">
                操作日志
                <n-switch v-model:value="operationLogEnabled" size="small" class="operation-switch">
                  <template #checked>启用</template>
                  <template #unchecked>关闭</template>
                </n-switch>
              </div>
            </div>
            
            <!-- 操作日志和告警日志之间的分割线 -->
            <div class="log-type-separator"></div>
            
            <div class="log-type-section">
              <div class="log-type-title">
                告警日志
                <n-switch v-model:value="alertLogEnabled" size="small" class="alert-switch">
                  <template #checked>启用</template>
                  <template #unchecked>关闭</template>
                </n-switch>
              </div>
              

            </div>
            
            <!-- 告警日志和框架日志之间的分割线 -->
            <div class="log-type-separator"></div>
            
            <div class="log-type-section" :class="{ 
              'has-filtered-data': areaFilterStatus.framework === true && (debouncedSearchText || (startTime && endTime)),
              'no-filtered-data': areaFilterStatus.framework === false && (debouncedSearchText || (startTime && endTime))
            }">
              <div class="log-type-title">
                框架日志
                <n-switch v-model:value="frameworkLogEnabled" size="small" class="framework-switch">
                  <template #checked>启用</template>
                  <template #unchecked>关闭</template>
                </n-switch>
              </div>
            </div>
            <div class="debug-log-section">
              <div class="debug-log-header">
                <div class="debug-log-title">调试日志</div>
                <!-- 调试日志组件搜索框 -->
                <div class="debug-log-search">
                  <n-input
                    v-model:value="debugLogSearchText"
                    placeholder="搜索组件..."
                    size="small"
                    clearable
                    @update:value="onDebugLogSearchChange"
                  >
                    <template #prefix>
                      <n-icon><SearchOutline /></n-icon>
                    </template>
                  </n-input>
                </div>
              </div>
              <div class="component-list">
                <div
                  v-for="option in filteredComponentOptions"
                  :key="option.value"
                  class="component-item-wrapper"
                  :class="{ 
                    'component-no-logs': !hasComponentLogs(option.value || ''),
                    'has-filtered-data': componentFilterStatus.get(option.value || '') === true,
                    'no-filtered-data': componentFilterStatus.has(option.value || '') && componentFilterStatus.get(option.value || '') === false
                  }"
                >
                  <n-button
                    quaternary
                    circle
                    size="tiny"
                    @click="openMdbDrawer(option.value || '')"
                    class="mdb-btn component-mdb-btn"
                    title="查看组件MDB信息"
                  >
                    <template #icon>
                      <span class="mdb-text">mdb</span>
                    </template>
                  </n-button>
                  <div class="mdb-separator"></div>
                  <n-checkbox
                    :checked="selectedComponents.includes(option.value || '')"
                    @update:checked="(checked) => toggleComponent(option.value || '', checked)"
                    class="component-checkbox"
                    :disabled="!hasComponentLogs(option.value || '')"
                  >
                    {{ option.label }}
                  </n-checkbox>
                </div>
                <div v-if="filteredComponentOptions.length === 0" class="no-components">
                  <div class="no-components-text">
                    {{ debouncedDebugLogSearchText && debouncedDebugLogSearchText.trim() !== '' ? '未找到匹配的组件' : '暂无可用组件' }}
                  </div>
                  <div v-if="debouncedDebugLogSearchText && debouncedDebugLogSearchText.trim() !== ''" class="search-tip">
                    搜索关键词: "{{ debouncedDebugLogSearchText }}"
                  </div>
                </div>
              </div>
            </div>
          </div>
        </n-card>
      </div>

      <!-- 右侧：筛选条件和日志内容区域 -->
      <div class="right-panel">
        <n-space vertical size="large">
          <!-- 筛选区域 -->
          <n-card size="small" class="filter-card" title="筛选条件">
            <div class="filter-row">
              <div class="filter-item">
                <n-date-picker
                  v-model:value="startTime"
                  type="datetime"
                  placeholder="选择开始时间"
                  style="width: 200px"
                  clearable
                  @update:value="onTimeChange"
                />
              </div>
              <div class="filter-item">
                <n-date-picker
                  v-model:value="endTime"
                  type="datetime"
                  placeholder="选择结束时间"
                  style="width: 200px"
                  clearable
                  @update:value="onTimeChange"
                />
              </div>
              <div class="filter-item">
                <n-input
                  v-model:value="searchText"
                  placeholder="输入搜索关键词"
                  style="width: 360px"
                  clearable
                  @update:value="onSearchChange"
                >
                  <template #prefix>
                    <n-icon><SearchOutline /></n-icon>
                  </template>
                </n-input>
              </div>
              <div class="filter-item">
                <div class="exclude-search-container">
                  <div class="exclude-input-wrapper">
                    <n-input
                      v-model:value="excludeSearchInput"
                      placeholder="输入要排除的关键词... 按回车确认"
                      class="exclude-search-input"
                      clearable
                      @keyup.enter="addExcludeKeyword"
                      @update:value="onExcludeSearchInputChange"
                    >
                      <template #prefix>
                        <n-icon><TrashOutline /></n-icon>
                      </template>
                      <template #suffix v-if="excludeKeywords.length > 0">
                        <div class="exclude-tags-inline">
                          <!-- 显示标签 -->
                          <div
                            v-for="(keyword, index) in excludeKeywords"
                            :key="index"
                            class="exclude-tag-inline"
                          >
                            <span class="exclude-tag-text">{{ keyword }}</span>
                            <span
                              class="exclude-tag-remove-x"
                              @click="removeExcludeKeyword(index)"
                            >×</span>
                          </div>

                        </div>
                      </template>
                    </n-input>

                  </div>
                </div>
              </div>
            </div>
          </n-card>

      <!-- 日志内容区域 -->


      <!-- 操作日志区域 -->
      <div v-if="operationLogEnabled" class="log-content-area resizable-area" :style="{ height: operationLogHeight + 'px' }">
        <div class="component-content">
          <!-- 左侧统计面板 - 已隐藏，但功能逻辑保留 -->
          <div class="stats-panel operation-stats-panel hidden-stats" :class="{ 
            'has-filtered-data': areaFilterStatus.operation === true && (debouncedSearchText || (startTime && endTime)),
            'no-filtered-data': areaFilterStatus.operation === false && (debouncedSearchText || (startTime && endTime))
          }">
            <!-- 统计信息已隐藏，但功能逻辑保留 -->
          </div>

          <!-- 右侧日志内容 -->
          <div class="logs-panel">
            <n-card size="small" class="logs-card">
              <!-- 操作日志搜索框和操作日志名称 -->
              <div class="component-search-bar">
                <!-- 删除按钮 - 移动到最左边 -->
                <n-button 
                  quaternary 
                  circle 
                  size="small" 
                  @click="removeOperationLog"
                  class="delete-btn"
                  title="删除操作日志"
                >
                  <template #icon>
                    <n-icon><TrashOutline /></n-icon>
                  </template>
                </n-button>
                
                <!-- 操作日志名称 -->
                <div class="component-header">
                  <span class="component-name">操作日志({{ getOperationLogs().length }})</span>
                </div>
                
                <n-input
                  :value="operationSearchText || ''"
                  placeholder="搜索操作日志..."
                  class="operation-search-input"
                  clearable
                  @update:value="onOperationSearchChange"
                >
                  <template #prefix>
                    <n-icon><SearchOutline /></n-icon>
                  </template>
                </n-input>
                
                <!-- Interface多选下拉列表 -->
                <n-select
                  v-model:value="selectedOperationInterfaces"
                  :options="operationInterfaceOptions"
                  multiple
                  placeholder="选择Interface"
                  class="interface-select"
                  size="small"
                  @update:value="onOperationInterfaceChange"
                  :render-tag="renderInterfaceTag"
                />
              </div>
              
              <div v-if="getOperationLogs().length > 0" class="log-content operation-logs">
                <n-virtual-list
                  :key="`operation-${operationVirtualListKey}`"
                  :items="getOperationLogs()"
                  :item-size="36"
                  :item-resizable="true"
                  container-style="height: 100%;"
                  :show-scrollbar="true"
                >
                  <template #default="{ item, index }">
                    <div 
                      class="log-item" 
                      :key="`operation-${index}-${item.timestamp}`" 
                      :class="{ 'log-item-even': (getOperationLogs().indexOf(item) + 1) % 2 === 0 }"
                    >
                      <div class="log-line">
                        <span class="log-line-number">{{ getOperationLogs().indexOf(item) + 1 }}</span>
                        <span class="drag-handle" 
                              draggable="true"
                              @dragstart="handleLogDragStart($event, item, 'operation')"
                              title="拖拽到日志篮">
                          <n-icon size="14">
                            <MenuOutline />
                          </n-icon>
                        </span>
                        <span class="log-time selectable-text" 
                              :data-log-content="item.timestamp"
                              @contextmenu.prevent="showContextMenu($event, item, 'operation')"
                              @dblclick="selectLogText($event, item, 'operation', 'timestamp')">
                          {{ item.timestamp }}
                        </span>
                        <span class="log-component" :class="getInterfaceClass(item.interface)">{{ item.interface }}</span>
                        <span class="log-message selectable-text" 
                              :data-log-content="`${item.timestamp} ${item.interface} ${item.user},${item.component},${item.message}`"
                              @dblclick="selectLogText($event, item, 'operation')">
                          {{ item.user }},{{ item.component }},{{ item.message }}
                        </span>
                        <div class="log-actions">
                          <n-button 
                            quaternary 
                            circle 
                            size="tiny" 
                            @click="copyLogText(item, 'operation')"
                            class="copy-btn"
                            title="复制日志内容"
                          >
                            <template #icon>
                              <n-icon><DocumentTextOutline /></n-icon>
                            </template>
                          </n-button>

                        </div>
                      </div>
                    </div>
                  </template>
                </n-virtual-list>
              </div>
              <div v-else class="empty-logs">
                <n-empty description="暂无操作日志数据">
                  <template #extra>
                    <n-text>请检查操作日志文件是否存在</n-text>
                  </template>
                </n-empty>
              </div>
            </n-card>
          </div>
        </div>
        <!-- 拖拽调整大小手柄 -->
        <div class="resize-handle" 
             @mousedown="startResize('operation', $event)"
             title="拖拽调整大小">
          <div class="resize-handle-line"></div>
          <div class="resize-handle-dots">
            <span class="resize-dot"></span>
            <span class="resize-dot"></span>
            <span class="resize-dot"></span>
          </div>
        </div>
      </div>

      <!-- 告警日志区域 -->
      <div v-if="alertLogEnabled" class="log-content-area resizable-area" :style="{ height: alertLogHeight + 'px' }">
        <div class="component-content">
          <!-- 左侧统计面板 - 已隐藏，但功能逻辑保留 -->
          <div class="stats-panel alert-stats-panel hidden-stats">
            <!-- 统计信息已隐藏，但功能逻辑保留 -->
          </div>

          <!-- 右侧日志内容 -->
          <div class="logs-panel">
            <n-card size="small" class="logs-card">
              <!-- 告警日志搜索框和操作按钮 -->
              <div class="component-search-bar">
                <!-- 删除按钮 - 移动到最左边 -->
                <n-button 
                  quaternary 
                  circle 
                  size="small" 
                  @click="removeAlertLog"
                  class="delete-btn"
                  title="删除告警日志"
                >
                  <template #icon>
                    <n-icon><TrashOutline /></n-icon>
                  </template>
                </n-button>
                
                <!-- 告警日志名称 -->
                <div class="component-header">
                  <span class="component-name">告警日志({{ filteredAlertLogs.length }})</span>
                </div>
                
                <n-input
                  v-model:value="alertLogSearchText"
                  placeholder="搜索告警日志..."
                  class="alert-search-input"
                  clearable
                >
                  <template #prefix>
                    <n-icon><SearchOutline /></n-icon>
                  </template>
                </n-input>
              </div>

              <!-- 告警日志表格内容 -->
              <div class="alert-log-content">
                <div v-if="alertLogs.length === 0" class="no-logs-message">
                  暂无告警日志数据
                </div>
                <div v-else class="alert-log-table-wrapper">
                  <!-- 数据表格 -->
                  <n-data-table
                    :columns="alertLogColumns"
                    :data="filteredAlertLogs"
                    :pagination="false"
                    :max-height="1000"
                    :scroll-x="600"
                    size="small"
                    striped
                    class="alert-log-table"
                    :bordered="false"
                    :single-line="false"
                    @update:filters="handleAlertLogFiltersUpdate"
                  />
                </div>
              </div>
            </n-card>
          </div>
        </div>
        <!-- 拖拽调整大小手柄 -->
        <div class="resize-handle" 
             @mousedown="startResize('alert', $event)"
             title="拖拽调整大小">
          <div class="resize-handle-line"></div>
          <div class="resize-handle-dots">
            <span class="resize-dot"></span>
            <span class="resize-dot"></span>
            <span class="resize-dot"></span>
          </div>
        </div>
      </div>

      <!-- 框架日志区域 -->
      <div v-if="frameworkLogEnabled" class="log-content-area resizable-area" :style="{ height: frameworkLogHeight + 'px' }">
        <div class="component-content">
          <!-- 左侧统计面板 - 已隐藏，但功能逻辑保留 -->
          <div class="stats-panel framework-stats-panel hidden-stats">
            <!-- 统计信息已隐藏，但功能逻辑保留 -->
          </div>

          <!-- 右侧日志内容 -->
          <div class="logs-panel">
            <n-card size="small" class="logs-card">
              <!-- 框架日志搜索框和操作按钮 -->
              <div class="component-search-bar">
                <!-- 删除按钮 - 移动到最左边 -->
                <n-button 
                  quaternary 
                  circle 
                  size="small" 
                  @click="removeFrameworkLog"
                  class="delete-btn"
                  title="删除框架日志"
                >
                  <template #icon>
                    <n-icon><TrashOutline /></n-icon>
                  </template>
                </n-button>
                
                <!-- 框架日志名称 -->
                <div class="component-header">
                  <span class="component-name">框架日志({{ getFilteredFrameworkLogsForDisplay().length }})</span>
                </div>
                
                <n-input
                  :value="frameworkSearchText || ''"
                  placeholder="搜索框架日志..."
                  class="framework-search-input"
                  clearable
                  @update:value="onFrameworkSearchChange"
                >
                  <template #prefix>
                    <n-icon><SearchOutline /></n-icon>
                  </template>
                </n-input>
                
                <!-- 框架日志级别开关 -->
                <div class="level-filters">
                  <div v-for="level in getFrameworkLogLevels()" :key="level" class="level-filter-item">
                    <n-switch 
                      :value="isFrameworkLevelEnabled(level)" 
                      size="small" 
                      @update:value="(value: boolean) => toggleFrameworkLevelFilter(level, value)"
                    >
                      <template #checked>{{ level }}({{ getFrameworkLogLevelCount(level) }})</template>
                      <template #unchecked>{{ level }}({{ getFrameworkLogLevelCount(level) }})</template>
                    </n-switch>
                  </div>
                </div>
              </div>
              
                                           <div v-if="getFilteredFrameworkLogsForDisplay().length > 0" class="log-content framework-logs">
                <n-virtual-list
                  :key="`framework-${frameworkVirtualListKey}`"
                  :items="getFilteredFrameworkLogsForDisplay()"
                  :item-size="36"
                  :item-resizable="true"
                  container-style="height: 100%;"
                  :show-scrollbar="true"
                >
                  <template #default="{ item, index }">
                    <div 
                      class="log-item" 
                      :key="`framework-${index}-${item.timestamp}`" 
                      :class="{ 'log-item-even': (getFilteredFrameworkLogsForDisplay().indexOf(item) + 1) % 2 === 0 }"
                    >
                      <div class="log-line">
                        <span class="log-line-number">{{ getFilteredFrameworkLogsForDisplay().indexOf(item) + 1 }}</span>
                        <span class="drag-handle" 
                              draggable="true"
                              @dragstart="handleLogDragStart($event, item, 'framework')"
                              title="拖拽到日志篮">
                          <n-icon size="14">
                            <MenuOutline />
                          </n-icon>
                        </span>
                        <span class="log-time selectable-text" 
                              :data-log-content="item.timestamp"
                              @contextmenu.prevent="showContextMenu($event, item, 'framework')"
                              @dblclick="selectLogText($event, item, 'framework', 'timestamp')">
                          {{ item.timestamp }}
                        </span>
                        <span class="log-component" v-if="item.component">{{ item.component }}</span>
                        <span :class="getLogLevelClass(item.level)">{{ item.level }}</span>
                        <span class="log-message selectable-text" 
                              :data-log-content="`${item.timestamp} ${item.component || ''} ${item.level} ${item.message}`"
                              @dblclick="selectLogText($event, item, 'framework')">
                          {{ item.message }}
                        </span>
                        <div class="log-actions">
                          <n-button 
                            quaternary 
                            circle 
                            size="tiny" 
                            @click="copyLogText(item, 'framework')"
                            class="copy-btn"
                            title="复制日志内容"
                          >
                            <template #icon>
                              <n-icon><DocumentTextOutline /></n-icon>
                            </template>
                          </n-button>

                        </div>
                      </div>
                    </div>
                  </template>
                </n-virtual-list>
              </div>
              <div v-else class="empty-logs">
                <n-empty description="暂无框架日志数据">
                  <template #extra>
                    <n-text>请检查框架日志文件是否存在</n-text>
                  </template>
                </n-empty>
              </div>
            </n-card>
          </div>
        </div>
        <!-- 拖拽调整大小手柄 -->
        <div class="resize-handle" 
             @mousedown="startResize('framework', $event)"
             title="拖拽调整大小">
          <div class="resize-handle-line"></div>
          <div class="resize-handle-dots">
            <span class="resize-dot"></span>
            <span class="resize-dot"></span>
            <span class="resize-dot"></span>
          </div>
        </div>
      </div>

              <!-- 调试日志内容区域 -->
      <div v-for="component in selectedComponents" :key="component" v-if="selectedComponents.length > 0" class="log-content-area resizable-area" :style="{ height: getComponentLogHeight(component) + 'px' }">
        <div class="component-content">
                     <!-- 左侧统计面板 - 隐藏显示但保留功能 -->
           <div class="stats-panel hidden-stats" :class="{ 'has-filtered-data': areaFilterStatus.debug }">
             <!-- 统计信息已隐藏，但功能逻辑保留 -->
           </div>

                     <!-- 右侧日志内容 -->
           <div class="logs-panel">
             <n-card size="small" class="logs-card">
               <!-- 组件内搜索框和日志等级开关 -->
               <div class="component-search-bar">
                 <!-- 删除按钮 - 移动到最左边 -->
                 <n-button 
                   quaternary 
                   circle 
                   size="small" 
                   @click="removeComponent(component)"
                   class="delete-btn"
                   title="删除组件"
                 >
                   <template #icon>
                     <n-icon><TrashOutline /></n-icon>
                   </template>
                 </n-button>
                 
                 <!-- 组件名称 -->
                 <div class="component-header">
                   <span class="component-name">{{ component }}({{ getComponentLogs(component).length }})</span>
                 </div>
                 
                 <n-input
                   :value="componentSearchTexts.get(component) || ''"
                   placeholder="搜索当前组件日志..."
                   class="component-search-input"
                   clearable
                   @update:value="(value: string) => onComponentSearchChange(component, value)"
                 >
                   <template #prefix>
                     <n-icon><SearchOutline /></n-icon>
                   </template>
                 </n-input>
                 
                 <!-- 日志等级开关 -->
                 <div class="level-filters">
                   <div v-for="level in getComponentLogLevels(component)" :key="level" class="level-filter-item">
                     <n-switch
                       :value="isLevelEnabled(component, level)"
                       size="small"
                       @update:value="(value: boolean) => toggleLevelFilter(component, level, value)"
                     >
                       <template #checked>{{ level }}({{ getComponentLogLevelCount(component, level) }})</template>
                       <template #unchecked>{{ level }}({{ getComponentLogLevelCount(component, level) }})</template>
                     </n-switch>
                   </div>
                 </div>
               </div>
               
               <div v-if="getComponentLogs(component).length > 0" class="log-content component-logs" :data-component="component">
                 <n-virtual-list
                   :key="`component-${component}-${componentVirtualListKeys.get(component) || 0}`"
                   :items="getComponentLogs(component)"
                   :item-size="36"
                   :item-resizable="true"
                   container-style="height: 100%;"
                   :show-scrollbar="true"
                 >
                   <template #default="{ item, index }">
                     <div 
                       class="log-item"
                       :class="{ 'log-item-even': (getComponentLogs(component).indexOf(item) + 1) % 2 === 0 }"
                     >
                       <div class="log-line">
                         <span class="log-line-number">{{ getComponentLogs(component).indexOf(item) + 1 }}</span>
                                                 <span class="drag-handle" 
                              draggable="true"
                              @dragstart="handleLogDragStart($event, item, 'debug', component)"
                              title="拖拽到日志篮">
                           <n-icon size="14">
                             <MenuOutline />
                           </n-icon>
                         </span>
                         <span class="log-time selectable-text" 
                               :data-log-content="item.timestamp"
                               @contextmenu.prevent="showContextMenu($event, item, 'runtime', component)"
                               @dblclick="selectLogText($event, item, 'runtime', component, 'timestamp')">
                           {{ item.timestamp }}
                         </span>
                         <span class="log-component">{{ item.component }}</span>
                         <span :class="getLogLevelClass(item.level)">{{ item.level }}</span>
                         <span class="log-message selectable-text" 
                               :data-log-content="`${item.timestamp} ${item.component} ${item.level} ${item.message}`"
                               @dblclick="selectLogText($event, item, 'runtime', component)">
                           {{ item.message }}
                         </span>
                         <div class="log-actions">
                           <n-button 
                             quaternary 
                             circle 
                             size="tiny" 
                             @click="copyLogText(item, 'runtime', component)"
                             class="copy-btn"
                             title="复制日志内容"
                           >
                             <template #icon>
                               <n-icon><DocumentTextOutline /></n-icon>
                             </template>
                           </n-button>
                         </div>
                       </div>
                     </div>
                   </template>
                 </n-virtual-list>
               </div>
               <div v-else class="empty-logs">
                 <n-empty description="暂无日志数据">
                   <template #extra>
                     <n-text>请选择组件并加载日志</n-text>
                   </template>
                 </n-empty>
               </div>
             </n-card>
           </div>
        </div>
        <!-- 拖拽调整大小手柄 -->
        <div class="resize-handle" 
             @mousedown="startResize('component-' + component, $event)"
             title="拖拽调整大小">
          <div class="resize-handle-line"></div>
          <div class="resize-handle-dots">
            <span class="resize-dot"></span>
            <span class="resize-dot"></span>
            <span class="resize-dot"></span>
          </div>
        </div>
      </div>
     </n-space>
     
     <!-- MDB 信息抽屉 -->
     <n-drawer
       v-model:show="mdbDrawerShow"
       :width="800"
       placement="right"
       :trap-focus="false"
       :block-scroll="false"
     >
       <n-drawer-content
         :title="getMdbDrawerTitle()"
         closable
         @close="closeMdbDrawer"
       >
                   <div class="mdb-drawer-content">
                     <!-- 固定的搜索区域 -->
                     <div class="mdb-search-container">
                       <!-- 搜索框 -->
                       <div class="mdb-search-bar">
                         <n-input
                           v-model:value="mdbSearchText"
                           :placeholder="getMdbSearchPlaceholder()"
                           style="width: 100%;"
                           clearable
                           @update:value="onMdbSearchChange"
                           @keydown.enter="onSearchEnter"
                         >
                           <template #prefix>
                             <n-icon><SearchOutline /></n-icon>
                           </template>
                         </n-input>
                       </div>
                       
                       <!-- 搜索导航 -->
                       <div v-if="mdbSearchText.trim() && searchMatches.length > 0" class="mdb-search-nav">
                         <div class="search-info">
                           <n-text>{{ currentMatchIndex + 1 }} / {{ searchMatches.length }}</n-text>
                         </div>
                         <div class="search-controls">
                           <n-button 
                             size="small" 
                             @click="goToPreviousMatch"
                             :disabled="currentMatchIndex <= 0"
                           >
                             上一个
                           </n-button>
                           <n-button 
                             size="small" 
                             @click="goToNextMatch"
                             :disabled="currentMatchIndex >= searchMatches.length - 1"
                           >
                             下一个
                           </n-button>
                         </div>
                       </div>
                     </div>
                     
                     <!-- 可滚动的内容区域 -->
                     <div class="mdb-scrollable-content">
                       <div v-if="mdbDrawerContent" class="mdb-content">
                         <!-- CSR内容特殊处理 -->
                         <div v-if="currentMdbComponent === 'BMC'" class="csr-content">

                           
                           <div class="csr-text-content">
                             <div 
                               v-for="(item, index) in highlightedCSRContent" 
                               :key="index"
                               class="csr-text-line"
                               :class="{
                                 'connector-presence-active': item.connectorName && item.presence === 1,
                                 'connector-presence-inactive': item.connectorName && item.presence !== 1 && item.presence !== undefined
                               }"
                               :style="{ paddingLeft: (item.level * 20 + 10) + 'px' }"
                             >
                               <span class="csr-text" v-html="item.highlightedText"></span>
                               <n-button
                                 v-if="item.connectorName"
                                 size="tiny"
                                 type="primary"
                                 ghost
                                 @click="viewConnectorDetail(item.connectorName)"
                                 class="connector-detail-btn hover-only"
                                 title="查看Connector详细信息"
                               >
                                 查看CSR信息
                               </n-button>
                             </div>
                           </div>
                         </div>
                         <!-- MDB内容特殊处理 - 添加整行高亮 -->
                         <div v-else class="mdb-content-text">
                           <div class="mdb-text-lines">
                             <div 
                               v-for="(line, index) in highlightedMdbContentLines" 
                               :key="index"
                               class="mdb-text-line"
                               :class="{ 'search-highlight-current': isCurrentMdbMatch(index) }"
                             >
                               <span v-html="line"></span>
                             </div>
                           </div>
                         </div>
                       </div>
                       <div v-else-if="mdbDrawerLoading" class="mdb-loading">
                         <n-spin size="medium" />
                         <div style="margin-top: 8px;">加载中...</div>
                       </div>
                       <div v-else class="mdb-error">
                         <n-empty :description="getMdbErrorDescription()">
                           <template #extra>
                             <n-text>{{ getMdbErrorExtraText() }}</n-text>
                           </template>
                         </n-empty>
                       </div>
                     </div>
                   </div>
                 </n-drawer-content>
     </n-drawer>
     
     <!-- Connector详细信息抽屉 -->
     <n-drawer
       v-model:show="connectorDetailDrawerShow"
       :width="800"
       placement="left"
       :trap-focus="false"
       :block-scroll="false"
     >
       <n-drawer-content
         :title="`Connector详细信息 - ${currentConnectorDetailFileName || '未知文件'}`"
         closable
         @close="closeConnectorDetailDrawer"
       >
         <div class="connector-detail-content">
           <div v-if="currentConnectorDetail" class="connector-detail-text">
             <!-- 固定的搜索区域 -->
             <div class="connector-detail-search-container">
               <!-- 搜索框 -->
               <div class="connector-detail-search-bar">
                 <n-input
                   v-model:value="connectorDetailSearchText"
                   placeholder="搜索 Connector 详细信息内容... (按回车搜索下一个)"
                   style="width: 100%;"
                   clearable
                   @update:value="onConnectorDetailSearchChange"
                   @keydown.enter="onConnectorDetailSearchEnter"
                 >
                   <template #prefix>
                     <n-icon><SearchOutline /></n-icon>
                   </template>
                 </n-input>
               </div>
               
               <!-- 搜索导航 -->
               <div v-if="connectorDetailSearchText.trim() && connectorDetailSearchMatches.length > 0" class="connector-detail-search-nav">
                 <div class="search-info">
                   <n-text>{{ connectorDetailCurrentMatchIndex + 1 }} / {{ connectorDetailSearchMatches.length }}</n-text>
                 </div>
                 <div class="search-buttons">
                   <n-button
                     size="tiny"
                     @click="connectorDetailPrevMatch"
                     :disabled="connectorDetailCurrentMatchIndex <= 0"
                   >
                     上一个
                   </n-button>
                   <n-button
                     size="tiny"
                     @click="connectorDetailNextMatch"
                     :disabled="connectorDetailCurrentMatchIndex >= connectorDetailSearchMatches.length - 1"
                   >
                     下一个
                   </n-button>
                 </div>
               </div>
             </div>
             
             <!-- 可滚动的内容区域 -->
             <div class="connector-detail-scrollable-content">
               <pre v-html="formattedConnectorDetailContent"></pre>
             </div>
           </div>
           <div v-else class="connector-detail-error">
             <n-empty description="未找到Connector详细信息">
               <template #extra>
                 <n-text>无法读取Connector详细信息文件</n-text>
               </template>
             </n-empty>
           </div>
         </div>
       </n-drawer-content>
     </n-drawer>

     <!-- BMC版本信息抽屉 -->
     <n-drawer
       v-model:show="bmcVersionDrawerShow"
       :width="800"
       placement="right"
       :trap-focus="false"
       :block-scroll="false"
     >
       <n-drawer-content
         title="BMC版本信息"
         closable
         @close="closeBmcVersionDrawer"
       >
         <div class="bmc-version-drawer-content">
           <div v-if="bmcVersionContent" class="bmc-version-content">
             <div class="bmc-version-content-text">
               <pre>{{ bmcVersionContent }}</pre>
             </div>
           </div>
           <div v-else-if="bmcVersionLoading" class="bmc-version-loading">
             <n-spin size="medium" />
             <div style="margin-top: 8px;">加载中...</div>
           </div>
           <div v-else class="bmc-version-error">
             <n-empty description="未找到BMC版本信息">
               <template #extra>
                 <n-text>无法读取BMC版本文件</n-text>
               </template>
             </n-empty>
           </div>
         </div>
       </n-drawer-content>
     </n-drawer>
     
     <!-- APP版本信息抽屉 -->
     <n-drawer
       v-model:show="appVersionDrawerShow"
       :width="800"
       placement="right"
       :trap-focus="false"
       :block-scroll="false"
     >
       <n-drawer-content
         title="APP版本信息"
         closable
         @close="closeAppVersionDrawer"
       >
         <div class="app-version-drawer-content">
           <!-- 固定的搜索区域 -->
           <div class="app-version-search-container">
             <!-- 搜索框 -->
             <div class="app-version-search-bar">
               <n-input
                 v-model:value="appVersionSearchText"
                 placeholder="搜索 APP 版本信息内容... (按回车搜索下一个)"
                 style="width: 100%;"
                 clearable
                 @update:value="onAppVersionSearchChange"
                 @keydown.enter="onAppVersionSearchEnter"
               >
                 <template #prefix>
                   <n-icon><SearchOutline /></n-icon>
                 </template>
               </n-input>
             </div>
             
             <!-- 搜索导航 -->
             <div v-if="appVersionSearchText.trim() && appVersionSearchMatches.length > 0" class="app-version-search-nav">
               <div class="search-info">
                 <n-text>{{ appVersionCurrentMatchIndex + 1 }} / {{ appVersionSearchMatches.length }}</n-text>
               </div>
               <div class="search-controls">
                 <n-button 
                   size="small" 
                   @click="goToPreviousAppVersionMatch"
                   :disabled="appVersionCurrentMatchIndex <= 0"
                 >
                   上一个
                 </n-button>
                 <n-button 
                   size="small" 
                   @click="goToNextAppVersionMatch"
                   :disabled="appVersionCurrentMatchIndex >= appVersionSearchMatches.length - 1"
                 >
                   下一个
                 </n-button>
               </div>
             </div>
           </div>
           
           <!-- 可滚动的内容区域 -->
           <div class="app-version-scrollable-content">
             <div v-if="appVersionContent" class="app-version-content">
               <div class="app-version-content-text">
                 <div class="app-version-text-lines">
                   <div 
                     v-for="(line, index) in highlightedAppVersionContentLines" 
                     :key="index"
                     class="app-version-text-line"
                     :class="{ 'search-highlight-current': isCurrentAppVersionMatch(index) }"
                   >
                     <span v-html="line"></span>
                   </div>
                 </div>
               </div>
             </div>
             <div v-else-if="appVersionLoading" class="app-version-loading">
               <n-spin size="medium" />
               <div style="margin-top: 8px;">加载中...</div>
             </div>
             <div v-else class="app-version-error">
               <n-empty description="未找到APP版本信息">
                 <template #extra>
                   <n-text>无法读取APP版本文件</n-text>
                 </template>
               </n-empty>
             </div>
           </div>
         </div>
       </n-drawer-content>
     </n-drawer>
     
     <!-- 时间分析抽屉 -->
                  <n-drawer
               v-model:show="timeAnalysisDrawerShow"
               :width="drawerWidth"
               placement="right"
               :trap-focus="false"
               :block-scroll="false"
             >
       <n-drawer-content
         title="时间范围分析"
         closable
         @close="closeTimeAnalysisDrawer"
       >
         <div class="time-analysis-content">
           <div class="time-info-section">
                             <div class="time-header">
                  <h3>选中日志时间</h3>
                  <div class="time-format-switch">
                    <n-button-group size="small">
                      <n-button 
                        :type="showTimeInSeconds ? 'primary' : 'default'"
                        @click="onTimeFormatChange(true)"
                        :disabled="showTimeInSeconds"
                      >
                        秒
                      </n-button>
                      <n-button 
                        :type="!showTimeInSeconds ? 'primary' : 'default'"
                        @click="onTimeFormatChange(false)"
                        :disabled="!showTimeInSeconds"
                      >
                        分钟
                      </n-button>
                    </n-button-group>
                  </div>
                </div>
             <div class="selected-time">
               <div class="time-display">
                 <div class="time-section">
                                     <div v-if="!isEditingTime" class="time-text" @click="startEditTime">
                    <n-text>{{ selectedLogTime }}</n-text>
                    <span class="edit-hint">单击编辑</span>
                                            <n-icon class="edit-icon" size="16">
                          <CreateOutline />
                        </n-icon>
                  </div>
                   <div v-else class="time-edit">
                     <n-input
                       v-model:value="editingTimeValue"
                       size="small"
                       @keydown.enter="saveTimeEdit"
                       @keydown.esc="cancelTimeEdit"
                       @blur="saveTimeEdit"
                       ref="timeInputRef"
                       style="width: 200px;"
                     />
                   </div>
                 </div>
                 <div class="time-navigation">
                   <!-- 向前范围和向后范围选择器 -->
                   <div class="time-range-selectors">
                     <div class="range-selector">
                       <n-select
                         v-model:value="forwardRange"
                         :options="rangeOptions"
                         size="small"
                         style="width: 120px;"
                         placeholder="向前范围"
                         clearable
                       />
                     </div>
                     <div class="range-selector">
                       <n-select
                         v-model:value="backwardRange"
                         :options="rangeOptions"
                         size="small"
                         style="width: 120px;"
                         placeholder="向后范围"
                         clearable
                       />
                     </div>
                   </div>
                   
                   <n-button 
                     size="small" 
                     @click="goToPreviousSecond"
                     :disabled="!canNavigateTime"
                     :title="showTimeInSeconds ? '上一秒' : '上一分钟'"
                   >
                     {{ showTimeInSeconds ? '上一秒' : '上一分钟' }}
                   </n-button>
                   <n-button 
                     size="small" 
                     @click="goToNextSecond"
                     :disabled="!canNavigateTime"
                     :title="showTimeInSeconds ? '下一秒' : '下一分钟'"
                   >
                     {{ showTimeInSeconds ? '下一秒' : '下一分钟' }}
                   </n-button>
                 </div>
               </div>
             </div>
           </div>
           
           <!-- 操作日志详细信息 -->
           <div class="log-detail-section">
             <h3>操作日志详细信息</h3>
             <div class="log-detail-content" :style="{ height: timeAnalysisOperationLogHeight + 'px' }">
               <div v-if="getTimeFilteredOperationLogs().length > 0" class="log-list">
                 <div 
                   v-for="(item, index) in getTimeFilteredOperationLogs()" 
                   :key="`operation-detail-${index}-${item.timestamp}`"
                   class="log-detail-item"
                   :class="{ 'log-detail-item-even': (index + 1) % 2 === 0 }"
                 >
                   <div class="log-detail-line">
                     <span class="log-detail-line-number">{{ index + 1 }}</span>
                     <span class="drag-handle" 
                           draggable="true"
                           @dragstart="handleLogDragStart($event, item, 'operation')"
                           title="拖拽到日志篮">
                       <n-icon size="14">
                         <MenuOutline />
                       </n-icon>
                     </span>
                     <span class="log-detail-time">{{ item.timestamp }}</span>
                     <span class="log-detail-component" :class="getInterfaceClass(item.interface)">{{ item.interface }}</span>
                                             <span class="log-detail-message">{{ item.user }},{{ item.component }},{{ item.message }}</span>
                   </div>
                 </div>
               </div>
               <div v-else class="no-logs">
                 <n-text type="disabled">该时间点无操作日志</n-text>
               </div>
             </div>
             <!-- 拖拽调整大小手柄 -->
             <div class="time-analysis-resize-handle" 
                  @mousedown="startTimeAnalysisResize('operation', $event)"
                  title="拖拽调整大小">
               <div class="resize-handle-line"></div>
               <div class="resize-handle-dots">
                 <span class="resize-dot"></span>
                 <span class="resize-dot"></span>
                 <span class="resize-dot"></span>
               </div>
             </div>
           </div>

           <!-- 框架日志详细信息 -->
           <div class="log-detail-section">
             <h3>框架日志详细信息</h3>
             <div class="log-detail-content" :style="{ height: timeAnalysisFrameworkLogHeight + 'px' }">
               <div v-if="getTimeFilteredFrameworkLogs().length > 0" class="log-list">
                 <div 
                   v-for="(item, index) in getTimeFilteredFrameworkLogs()" 
                   :key="`framework-detail-${index}-${item.timestamp}`"
                   class="log-detail-item"
                   :class="{ 'log-detail-item-even': (index + 1) % 2 === 0 }"
                 >
                   <div class="log-detail-line">
                     <span class="log-detail-line-number">{{ index + 1 }}</span>
                     <span class="drag-handle" 
                           draggable="true"
                           @dragstart="handleLogDragStart($event, item, 'framework')"
                           title="拖拽到日志篮">
                       <n-icon size="14">
                         <MenuOutline />
                       </n-icon>
                     </span>
                     <span class="log-detail-time">{{ item.timestamp }}</span>
                     <span class="log-detail-component">{{ item.component }}</span>
                     <span class="log-detail-level" :class="getLogLevelClass(item.level)">{{ item.level }}</span>
                     <span class="log-detail-message">{{ item.message }}</span>
                   </div>
                 </div>
               </div>
               <div v-else class="no-logs">
                 <n-text type="disabled">该时间点无框架日志</n-text>
               </div>
             </div>
             <!-- 拖拽调整大小手柄 -->
             <div class="time-analysis-resize-handle" 
                  @mousedown="startTimeAnalysisResize('framework', $event)"
                  title="拖拽调整大小">
               <div class="resize-handle-line"></div>
               <div class="resize-handle-dots">
                 <span class="resize-dot"></span>
                 <span class="resize-dot"></span>
                 <span class="resize-dot"></span>
                 <span class="resize-dot"></span>
               </div>
             </div>
           </div>

           <!-- 调试日志详细信息 -->
           <div class="log-detail-section">
             <h3>调试日志详细信息</h3>
             <div class="log-detail-content" :style="{ height: timeAnalysisDebugLogHeight + 'px' }">
               <div v-if="getTimeFilteredRuntimeLogs().length > 0" class="log-list">
                 <div 
                   v-for="(item, index) in getTimeFilteredRuntimeLogs()" 
                   :key="`runtime-detail-${index}-${item.timestamp}`"
                   class="log-detail-item"
                   :class="{ 'log-detail-item-even': (index + 1) % 2 === 0 }"
                 >
                   <div class="log-detail-line">
                     <span class="log-detail-line-number">{{ index + 1 }}</span>
                     <span class="drag-handle" 
                           draggable="true"
                           @dragstart="handleLogDragStart($event, item, 'debug')"
                           title="拖拽到日志篮">
                       <n-icon size="14">
                         <MenuOutline />
                       </n-icon>
                     </span>
                     <span class="log-detail-time">{{ item.timestamp }}</span>
                     <span class="log-detail-component">{{ item.component }}</span>
                     <span class="log-detail-level" :class="getLogLevelClass(item.level)">{{ item.level }}</span>
                     <span class="log-detail-message">{{ item.message }}</span>
                   </div>
                 </div>
               </div>
               <div v-else class="no-logs">
                 <n-text type="disabled">该时间点无调试日志</n-text>
               </div>
             </div>
             <!-- 拖拽调整大小手柄 -->
             <div class="time-analysis-resize-handle" 
                  @mousedown="startTimeAnalysisResize('debug', $event)"
                  title="拖拽调整大小">
               <div class="resize-handle-line"></div>
               <div class="resize-handle-dots">
                 <span class="resize-dot"></span>
                 <span class="resize-dot"></span>
                 <span class="resize-dot"></span>
               </div>
             </div>
           </div>

         </div>
       </n-drawer-content>
     </n-drawer>
     
     <!-- 右键上下文菜单 -->
     <n-dropdown
       v-model:show="contextMenuShow"
       :options="contextMenuOptions"
       :x="contextMenuX"
       :y="contextMenuY"
       placement="bottom-start"
       trigger="manual"
       @clickoutside="hideContextMenu"
       @select="handleContextMenuSelect"
     />
     
     <!-- 滚动控制菜单 -->
     <n-dropdown
       v-model:show="scrollControlMenuShow"
       :options="scrollControlMenuOptions"
       :x="scrollControlMenuX"
       :y="scrollControlMenuY"
       placement="bottom-start"
       trigger="manual"
       @clickoutside="hideScrollControlMenu"
       @select="handleScrollControlMenuSelect"
     />
      </div>
    </div>
   </div>
 </template>

<script setup lang="ts">
import { ref, computed, onMounted, h, watch, onUnmounted, nextTick } from 'vue'
import { useWorkspaceStore } from '../stores/workspace'
import { useFileDataStore } from '../stores/fileData'
import { TrashOutline, SearchOutline, DocumentTextOutline, CreateOutline, MenuOutline, InformationCircleOutline, LibraryOutline } from '@vicons/ionicons5'
import { 
  NSwitch, 
  NDropdown, 
  NSpace, 
  NCard, 
  NSelect, 
  NDatePicker, 
  NInput, 
  NIcon, 
  NButton, 
  NDataTable, 
  NVirtualList, 
  NEmpty, 
  NText, 
  NDrawer, 
  NDrawerContent, 
  NSpin,
  NCheckbox
} from 'naive-ui'
import { 
  LOG_PATHS, 
  extractFileContent, 
  extractComponentList
} from '../config/logPaths'
import { getAllDumpLogs } from '../utils/archiveDb'
import pako from 'pako'

const workspaceStore = useWorkspaceStore()
const fileDataStore = useFileDataStore()

// 路径处理工具函数
function buildPlatformPath(basePath: string, fileName: string): string {
  const isWindows = navigator.platform.indexOf('Win') !== -1
  const pathSeparator = isWindows ? '\\' : '/'
  return `${basePath}${pathSeparator}${fileName}`
}

function getPlatformPathSeparator(): string {
  const isWindows = navigator.platform.indexOf('Win') !== -1
  return isWindows ? '\\' : '/'
}

// 响应式数据
const selectedComponents = ref<string[]>([])
const componentLogs = ref<Map<string, any[]>>(new Map())
const startTime = ref<number | null>(null)
const endTime = ref<number | null>(null)
const levelFilters = ref<Map<string, Set<string>>>(new Map()) // 组件 -> 启用的日志级别集合
const searchText = ref<string>('') // 全局搜索文本
const debouncedSearchText = ref<string>('') // 防抖后的全局搜索文本

// 排除关键词相关变量
const excludeSearchInput = ref<string>('') // 排除关键词输入框的值
const excludeKeywords = ref<string[]>([]) // 排除关键词列表

// 组件内搜索文本
const componentSearchTexts = ref<Map<string, string>>(new Map()) // 组件 -> 搜索文本
const debouncedComponentSearchTexts = ref<Map<string, string>>(new Map()) // 组件 -> 防抖后的搜索文本

// 缓存过滤后的日志，避免重复计算 - 性能优化
const filteredLogsCache = ref<Map<string, any[]>>(new Map())
const MAX_CACHE_SIZE = 100
const cacheCleanupThreshold = 80

// 智能缓存清理函数
const cleanupCache = (cache: Map<string, any[]>) => {
  if (cache.size > MAX_CACHE_SIZE) {
    const entries = Array.from(cache.entries())
    // 保留最近使用的条目
    const sortedEntries = entries.sort((a, b) => {
      // 这里可以根据实际使用情况添加LRU逻辑
      return 0
    })
    
    // 删除多余的条目
    const toDelete = sortedEntries.slice(0, cache.size - cacheCleanupThreshold)
    toDelete.forEach(([key]) => cache.delete(key))
  }
}
const cacheKey = computed(() => {
  const componentSearchKeys = Array.from(debouncedComponentSearchTexts.value.entries())
    .map(([component, text]) => `${component}:${text}`)
    .join('|')
  const excludeKeywordsStr = excludeKeywords.value.join('|')
  return `${debouncedSearchText.value}_${startTime.value}_${endTime.value}_${JSON.stringify(Array.from(levelFilters.value.entries()))}_${componentSearchKeys}_${excludeKeywordsStr}`
})

// 滚动位置管理
const scrollPositionMemory = ref<Map<string, number>>(new Map())
const autoScrollToBottom = ref<Map<string, boolean>>(new Map())
const userScrolled = ref<Map<string, boolean>>(new Map())
const scrollControlMenuShow = ref<boolean>(false)
const scrollControlMenuX = ref<number>(0)
const scrollControlMenuY = ref<number>(0)
const scrollControlMenuTarget = ref<string>('')

// 虚拟列表强制重渲染key
const frameworkVirtualListKey = ref<number>(0)
const operationVirtualListKey = ref<number>(0)
const componentVirtualListKeys = ref<Map<string, number>>(new Map())

// MDB 抽屉相关
const mdbDrawerShow = ref<boolean>(false)
const mdbDrawerContent = ref<string>('')
const mdbDrawerLoading = ref<boolean>(false)
const currentMdbComponent = ref<string>('')
const mdbSearchText = ref<string>('') // MDB 搜索文本
const searchMatches = ref<number[]>([]) // 搜索匹配位置
const currentMatchIndex = ref<number>(-1) // 当前匹配索引

// Connector详细信息相关变量
const currentConnectorDetail = ref<string>('')
const currentConnectorDetailFileName = ref<string>('')
const connectorDetailDrawerShow = ref<boolean>(false)

// 页面提示
const pageNotification = ref({
  show: false,
  message: '',
  type: 'warning' as 'info' | 'warning' | 'success' | 'error'
})

// 区域筛选状态指示
const areaFilterStatus = ref({
  operation: false,    // 操作日志区域是否有满足筛选条件的数据
  framework: false,    // 框架日志区域是否有满足筛选条件的数据
  debug: false         // 调试日志区域是否有满足筛选条件的数据（暂时保留，但不使用）
})

// 组件级别的筛选状态指示
const componentFilterStatus = ref<Map<string, boolean>>(new Map())

// 更新区域筛选状态
function updateAreaFilterStatus() {
  // 检查是否有有效的筛选条件
  const hasSearchCondition = debouncedSearchText.value && debouncedSearchText.value.trim() !== ''
  const hasTimeCondition = startTime.value && endTime.value
  
  // 如果没有筛选条件，所有区域都不显示绿色
  if (!hasSearchCondition && !hasTimeCondition) {
    areaFilterStatus.value.operation = false
    areaFilterStatus.value.framework = false
    areaFilterStatus.value.debug = false
    // 清空所有组件的筛选状态
    componentFilterStatus.value.clear()
    return
  }
  
  // 检查操作日志区域
  // 使用全局搜索文本和操作日志专用搜索文本的组合来检查过滤状态
  const hasOperationSearchCondition = (debouncedSearchText.value && debouncedSearchText.value.trim() !== '') || 
                                    (debouncedOperationSearchText.value && debouncedOperationSearchText.value.trim() !== '')
  
  if (hasOperationSearchCondition || hasTimeCondition) {
    const operationLogs = getFilteredOperationLogs()
    areaFilterStatus.value.operation = operationLogs.length > 0
  } else {
    areaFilterStatus.value.operation = false
  }
  
  // 检查框架日志区域
  const frameworkLogs = getFilteredFrameworkLogsForDisplay()
  areaFilterStatus.value.framework = frameworkLogs.length > 0
  
  // 检查每个组件的筛选状态（组件级别的绿色指示和置灰）
  
  // 清空之前的组件状态
  componentFilterStatus.value.clear()
  
  for (const option of componentOptions.value) {
    const component = option.value
    if (!component) continue
    
    // 直接使用 getFilteredComponentLogs，但先清理该组件的缓存确保重新计算
    const cacheKeyForComponent = `${component}_${cacheKey.value}`
    filteredLogsCache.value.delete(cacheKeyForComponent)
    
    const logs = getFilteredComponentLogs(component)
    
    // 设置该组件的筛选状态
    componentFilterStatus.value.set(component, logs.length > 0)
  }
  
  // 调试日志整体区域不再显示绿色
  areaFilterStatus.value.debug = false
  
  // 告警日志暂时固定为没有数据状态（因为目前没有独立的告警日志筛选函数）
  // 如果需要独立的告警日志筛选，可以后续添加
  // areaFilterStatus.value.alert = false  // 如果后续添加告警日志状态，可以取消注释

}

// Connector详细信息搜索相关变量
const connectorDetailSearchText = ref<string>('')
const connectorDetailSearchMatches = ref<number[]>([])
const connectorDetailCurrentMatchIndex = ref<number>(-1)

// 格式化Connector详细信息内容
const formattedConnectorDetailContent = computed(() => {
  if (!currentConnectorDetail.value) return ''
  
  let formattedContent = ''
  try {
    // 尝试解析JSON并格式化
    const parsed = JSON.parse(currentConnectorDetail.value)
    formattedContent = JSON.stringify(parsed, null, 2)
  } catch (error) {
    // 如果不是有效的JSON，直接返回原内容
    formattedContent = currentConnectorDetail.value
  }
  
  // 如果有搜索文本，添加高亮
  if (connectorDetailSearchText.value.trim()) {
    const searchTerm = connectorDetailSearchText.value.trim()
    const escapedSearchTerm = searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
    const regex = new RegExp(`(${escapedSearchTerm})`, 'gi')
    
    // 按行处理，为包含匹配的行添加行级高亮
    const lines = formattedContent.split('\n')
    const highlightedLines = lines.map(line => {
      if (regex.test(line)) {
        // 重置正则表达式状态
        regex.lastIndex = 0
        // 为包含匹配的行添加行级高亮类
        const highlightedLine = line.replace(regex, '<mark class="search-highlight">$1</mark>')
        return `<div class="search-highlight-line">${highlightedLine}</div>`
      }
      return line
    })
    
    formattedContent = highlightedLines.join('\n')
  }
  
  return formattedContent
})

// BMC版本抽屉相关
const bmcVersionDrawerShow = ref<boolean>(false)
const bmcVersionContent = ref<string>('')
const bmcVersionLoading = ref<boolean>(false)

// APP版本抽屉相关
const appVersionDrawerShow = ref<boolean>(false)
const appVersionContent = ref<string>('')
const appVersionLoading = ref<boolean>(false)
const appVersionSearchText = ref<string>('') // APP版本搜索文本
const appVersionSearchMatches = ref<number[]>([]) // APP版本搜索匹配位置
const appVersionCurrentMatchIndex = ref<number>(-1) // APP版本当前匹配索引

// 日志类型开关
const operationLogEnabled = ref<boolean>(false)
const alertLogEnabled = ref<boolean>(false)
const frameworkLogEnabled = ref<boolean>(false)

/**
 * 从 dump_info/AppDump 文件夹下遍历获取组件名称
 * @param fileData 解压后的文件数据
 * @returns 组件名称数组
 */
function extractComponentsFromAppDump(fileData: string): string[] {
  if (!fileData) {
    return []
  }
  
  const components = new Set<string>()
  
  // 使用单一正则表达式优化性能
  const componentPattern = /dump_info[\/\\]AppDump[\/\\]([^\/\\\s]+)/g
  let match
  
  while ((match = componentPattern.exec(fileData)) !== null) {
    const componentName = match[1]
    if (componentName && 
        componentName !== 'versioninfo' && 
        !componentName.startsWith('.') &&
        componentName.length > 0) {
      components.add(componentName)
    }
  }
  
  // 如果正则匹配失败，使用备用方法
  if (components.size === 0) {
    const lines = fileData.split('\n')
    const pathPatterns = [
      /dump_info\/AppDump\/([^\/\\]+)[\/\\]/,
      /dump_info\\AppDump\\([^\/\\]+)[\/\\]/
    ]
    
    for (const line of lines) {
      if (line.includes('dump_info') && line.includes('AppDump')) {
        for (const pattern of pathPatterns) {
          const match = line.match(pattern)
          if (match && match[1]) {
            const componentName = match[1]
            if (componentName && 
                componentName !== 'versioninfo' && 
                !componentName.startsWith('.') &&
                componentName.length > 0) {
              components.add(componentName)
              break
            }
          }
        }
      }
    }
  }
  
  return Array.from(components)
}

// 获取统计面板和日志列表的实际显示数据高度
const getActualContentHeight = (type: string) => {
  // 等待DOM更新完成
  nextTick(() => {
    // 这里可以添加延迟确保DOM完全渲染
  })
  
  let statsPanelHeight = 0
  let logsPanelHeight = 0
  
  if (type === 'operation') {
    // 操作日志统计面板已隐藏，高度为0
    statsPanelHeight = 0
    const logsPanel = document.querySelector('.operation-stats-panel + .logs-panel')
    
    if (logsPanel) {
      logsPanelHeight = logsPanel.scrollHeight
    }
  } else if (type === 'framework') {
    const statsPanel = document.querySelector('.framework-stats-panel')
    const logsPanel = document.querySelector('.framework-stats-panel + .logs-panel')
    
    if (statsPanel) {
      statsPanelHeight = statsPanel.scrollHeight
    }
    if (logsPanel) {
      logsPanelHeight = logsPanel.scrollHeight
    }
  } else if (type.startsWith('component-')) {
    const component = type.replace('component-', '')
    const statsPanel = document.querySelector(`.component-${component}-stats-panel`)
    const logsPanel = document.querySelector(`.component-${component}-stats-panel + .logs-panel`)
    
    if (statsPanel) {
      statsPanelHeight = statsPanel.scrollHeight
    }
    if (logsPanel) {
      logsPanelHeight = logsPanel.scrollHeight
    }
  }
  
  // 返回统计面板和日志列表的实际高度之和，加上一些边距
  // 最小高度为当前屏幕的1/3
  const minHeight = Math.floor(window.innerHeight / 3)
  return Math.max(minHeight, statsPanelHeight + logsPanelHeight + 50)
}

// 获取统计面板最小高度（保持向后兼容）
const getStatsPanelMinHeight = () => {
  // 最小高度为当前屏幕的1/3
  return Math.floor(window.innerHeight / 3)
}

// 拖拽调整大小相关
const operationLogHeight = ref<number>(Math.floor(window.innerHeight / 3)) // 初始高度为当前屏幕的1/3
const frameworkLogHeight = ref<number>(Math.floor(window.innerHeight / 3)) // 初始高度为当前屏幕的1/3
const alertLogHeight = ref<number>(Math.floor(window.innerHeight / 3)) // 初始高度为当前屏幕的1/3
const componentLogHeights = ref<Map<string, number>>(new Map())

// 时间分析页面日志显示框高度
const timeAnalysisOperationLogHeight = ref<number>(200) // 初始高度200px
const timeAnalysisFrameworkLogHeight = ref<number>(200) // 初始高度200px
const timeAnalysisDebugLogHeight = ref<number>(200) // 初始高度200px
const isResizing = ref<boolean>(false)
const currentResizeType = ref<string>('')
const startY = ref<number>(0)
const startHeight = ref<number>(0)

// 性能优化：缓存DOM元素引用
const resizeElements = ref<Map<string, HTMLElement>>(new Map())
const resizeAreas = ref<Map<string, HTMLElement>>(new Map())

// 性能优化：节流拖拽更新
const resizeUpdateThrottle = ref<number>(0)
const RESIZE_UPDATE_THROTTLE_MS = 16 // 约60fps

// 性能优化：批量更新标志
const pendingResizeUpdate = ref<boolean>(false)

// 性能优化：防抖函数
const debounce = (func: Function, delay: number) => {
  let timeoutId: ReturnType<typeof setTimeout>
  return (...args: any[]) => {
    clearTimeout(timeoutId)
    timeoutId = setTimeout(() => func.apply(null, args), delay)
  }
}

// 性能优化：节流函数
const throttle = (func: Function, delay: number) => {
  let lastCall = 0
  return (...args: any[]) => {
    const now = Date.now()
    if (now - lastCall >= delay) {
      lastCall = now
      func.apply(null, args)
    }
  }
}

// 性能优化：虚拟列表优化
const VIRTUAL_LIST_ITEM_HEIGHT = 40
const VIRTUAL_LIST_BUFFER_SIZE = 5

// 操作日志interface筛选
const interfaceFilters = ref<string[]>([]) // 禁用的interface集合

// 右键上下文菜单相关
const contextMenuShow = ref<boolean>(false)
const contextMenuX = ref<number>(0)
const contextMenuY = ref<number>(0)
const contextMenuLogItem = ref<any>(null)
const contextMenuLogType = ref<string>('')
const contextMenuComponent = ref<string>('')

// 时间分析抽屉相关
const timeAnalysisDrawerShow = ref<boolean>(false)
const selectedLogTime = ref<string>('')
const suggestedStartTime = ref<string>('')
const suggestedEndTime = ref<string>('')

const canNavigateTime = ref<boolean>(true)
// 抽屉宽度，与日志篮抽屉保持一致
const currentWindowWidth = ref(window.innerWidth)
const drawerWidth = computed(() => {
  // 抽屉宽度为当前窗口的一半
  const halfWidth = currentWindowWidth.value * 0.5
  return Math.max(600, halfWidth)
})

// 时间编辑相关
const isEditingTime = ref<boolean>(false)
const editingTimeValue = ref<string>('')
const timeInputRef = ref<any>(null)

// 时间格式显示相关
const showTimeInSeconds = ref<boolean>(true) // 默认按秒显示

// 向前和向后范围选择器
const forwardRange = ref<number | null>(null) // 向前范围，默认值为null（不选择）
const backwardRange = ref<number | null>(null) // 向后范围，默认值为null（不选择）
const rangeOptions = ref([
  { label: '1', value: 1 },
  { label: '2', value: 2 },
  { label: '3', value: 3 },
  { label: '4', value: 4 },
  { label: '5', value: 5 },
  { label: '6', value: 6 },
  { label: '7', value: 7 },
  { label: '8', value: 8 },
  { label: '9', value: 9 },
  { label: '10', value: 10 }
])

// 辅助函数：获取向前和向后范围的实际数值
const getForwardRangeValue = () => {
  if (forwardRange.value === null) return 0
  return forwardRange.value
}

const getBackwardRangeValue = () => {
  if (backwardRange.value === null) return 0
  return backwardRange.value
}

// 自动聚焦到新增日志板块的方法
const scrollToLogPanel = (panelType: string, componentName?: string) => {
  nextTick(() => {
    setTimeout(() => {
      let targetElement: HTMLElement | null = null
      
      if (panelType === 'operation') {
        // 查找操作日志面板
        targetElement = document.querySelector('.log-content-area:has(.operation-stats-panel)') as HTMLElement
      } else if (panelType === 'framework') {
        // 查找框架日志面板
        targetElement = document.querySelector('.log-content-area:has(.framework-stats-panel)') as HTMLElement
      } else if (panelType === 'component' && componentName) {
        // 查找特定组件的调试日志面板
        const componentPanels = document.querySelectorAll('.log-content-area')
        let targetIndex = -1
        
        // 通过组件名称在 selectedComponents 中的索引来定位面板
        if (selectedComponents.value.includes(componentName)) {
          targetIndex = selectedComponents.value.indexOf(componentName)
        }
        
        if (targetIndex >= 0) {
          // 找到对应的调试日志面板（跳过操作日志和框架日志面板）
          let debugPanelIndex = 0
          for (let i = 0; i < componentPanels.length; i++) {
            const panel = componentPanels[i] as HTMLElement
            // 检查是否是调试日志面板（不是操作日志或框架日志）
            if (!panel.querySelector('.operation-stats-panel') && !panel.querySelector('.framework-stats-panel')) {
              if (debugPanelIndex === targetIndex) {
                targetElement = panel
                break
              }
              debugPanelIndex++
            }
          }
        }
        
        // 如果没找到特定组件，找最后一个调试日志面板（新增的）
        if (!targetElement) {
          const allPanels = Array.from(componentPanels)
          const debugPanels = allPanels.filter(panel => 
            !panel.querySelector('.operation-stats-panel') && 
            !panel.querySelector('.framework-stats-panel')
          )
          targetElement = debugPanels[debugPanels.length - 1] as HTMLElement
        }
      }
      
      if (targetElement) {
        // 计算目标位置，让面板顶部距离视窗顶部有一定间距
        const rect = targetElement.getBoundingClientRect()
        const scrollTop = window.pageYOffset || document.documentElement.scrollTop
        const targetPosition = rect.top + scrollTop - 100 // 100px 间距
        
        // 平滑滚动到目标位置
        window.scrollTo({
          top: Math.max(0, targetPosition),
          behavior: 'smooth'
        })
        
        // 添加高亮效果
        targetElement.classList.add('newly-added-highlight')
        setTimeout(() => {
          targetElement?.classList.remove('newly-added-highlight')
        }, 2000) // 2秒后移除高亮
      }
    }, 200) // 延迟200ms确保DOM完全渲染
  })
}

// 时间分析页面拖拽调整大小相关方法
const startTimeAnalysisResize = (type: string, event: MouseEvent) => {
  isResizing.value = true
  currentResizeType.value = `time-analysis-${type}`
  startY.value = event.clientY
  
  // 记录初始高度
  if (type === 'operation') {
    startHeight.value = timeAnalysisOperationLogHeight.value
  } else if (type === 'framework') {
    startHeight.value = timeAnalysisFrameworkLogHeight.value
  } else if (type === 'debug') {
    startHeight.value = timeAnalysisDebugLogHeight.value
  }
  
  document.addEventListener('mousemove', handleTimeAnalysisResizeDrag, { passive: true })
  document.addEventListener('mouseup', stopTimeAnalysisResize)
  
  // 防止文本选择
  event.preventDefault()
}

const handleTimeAnalysisResizeDrag = (event: Event) => {
  if (!isResizing.value) return
  
  const mouseEvent = event as MouseEvent
  const deltaY = mouseEvent.clientY - startY.value
  
  // 计算新的高度
  let newHeight = startHeight.value + deltaY
  
  // 应用约束：最小高度100px，最大高度800px
  const minHeight = 100
  const maxHeight = 800
  
  newHeight = Math.max(minHeight, newHeight)
  newHeight = Math.min(maxHeight, newHeight)
  
  // 更新对应的高度值
  if (currentResizeType.value === 'time-analysis-operation') {
    timeAnalysisOperationLogHeight.value = newHeight
  } else if (currentResizeType.value === 'time-analysis-framework') {
    timeAnalysisFrameworkLogHeight.value = newHeight
  } else if (currentResizeType.value === 'time-analysis-debug') {
    timeAnalysisDebugLogHeight.value = newHeight
  }
}

const stopTimeAnalysisResize = () => {
  isResizing.value = false
  currentResizeType.value = ''
  document.removeEventListener('mousemove', handleTimeAnalysisResizeDrag)
  document.removeEventListener('mouseup', stopTimeAnalysisResize)
}

// 拖拽调整大小相关方法
const startResize = (type: string, event: MouseEvent) => {
  isResizing.value = true
  currentResizeType.value = type
  startY.value = event.clientY
  
  // 性能优化：缓存DOM元素引用
  const targetElement = event.target as HTMLElement
  const resizingArea = targetElement.closest('.resizable-area') as HTMLElement
  if (resizingArea) {
    resizingArea.classList.add('resizing')
    // 缓存拖拽区域引用
    resizeAreas.value.set(type, resizingArea)
  }
  
  // 记录初始高度
  if (type === 'operation') {
    startHeight.value = operationLogHeight.value
  } else if (type === 'framework') {
    startHeight.value = frameworkLogHeight.value
  } else if (type === 'alert') {
    startHeight.value = alertLogHeight.value
  } else if (type.startsWith('component-')) {
    const component = type.replace('component-', '')
          startHeight.value = componentLogHeights.value.get(component) || Math.floor(window.innerHeight / 3)
  }
  
  // 性能优化：只在拖拽开始时保存一次滚动位置
  if (!pendingResizeUpdate.value) {
    saveAllScrollPositions()
    pendingResizeUpdate.value = true
  }
  
  document.addEventListener('mousemove', handleResizeDrag, { passive: true })
  document.addEventListener('mouseup', stopResize)
  
  // 防止文本选择
  event.preventDefault()
}

const handleResizeDrag = (event: Event) => {
  if (!isResizing.value) return
  
  // 性能优化：节流拖拽更新，避免过于频繁的更新
  const now = Date.now()
  if (now - resizeUpdateThrottle.value < RESIZE_UPDATE_THROTTLE_MS) {
    return
  }
  resizeUpdateThrottle.value = now
  
  const mouseEvent = event as MouseEvent
  const deltaY = mouseEvent.clientY - startY.value
  
  // 性能优化：使用 requestAnimationFrame 优化拖拽性能
  requestAnimationFrame(() => {
    // 计算新的高度
    let newHeight = startHeight.value + deltaY
    
    // 获取当前窗口高度
    const windowHeight = window.innerHeight
    
    // 应用约束：最小不能小于当前屏幕大小的1/4，最大可以超过当前窗口大小以支持向下拖拽
    const minHeight = Math.floor(windowHeight / 4)
    
    // 检查是否有滚动条，如果没有滚动条则不允许拖大
    let maxHeight = windowHeight * 2 // 默认最大高度
    if (currentResizeType.value === 'operation') {
      const operationContainer = document.querySelector('.operation-logs .n-virtual-list') as HTMLElement
      if (operationContainer && operationContainer.scrollHeight <= operationContainer.clientHeight) {
        // 没有滚动条，限制最大高度为当前高度
        maxHeight = startHeight.value
      }
    } else if (currentResizeType.value === 'framework') {
      const frameworkContainer = document.querySelector('.framework-logs .n-virtual-list') as HTMLElement
      if (frameworkContainer && frameworkContainer.scrollHeight <= frameworkContainer.clientHeight) {
        // 没有滚动条，限制最大高度为当前高度
        maxHeight = startHeight.value
      }
    } else if (currentResizeType.value === 'alert') {
      // 告警日志允许拖拽到整个屏幕大小，不受滚动条限制
      // 保持默认的最大高度 windowHeight * 2
    } else if (currentResizeType.value.startsWith('component-')) {
      const component = currentResizeType.value.replace('component-', '')
      const componentContainer = document.querySelector(`[data-component="${component}"] .n-virtual-list`) as HTMLElement
      if (componentContainer && componentContainer.scrollHeight <= componentContainer.clientHeight) {
        // 没有滚动条，限制最大高度为当前高度
        maxHeight = startHeight.value
      }
    }
    
    newHeight = Math.max(minHeight, newHeight)
    newHeight = Math.min(maxHeight, newHeight)
    
    // 性能优化：直接更新高度值，避免重复计算
    if (currentResizeType.value === 'operation') {
      operationLogHeight.value = newHeight
    } else if (currentResizeType.value === 'framework') {
      frameworkLogHeight.value = newHeight
    } else if (currentResizeType.value === 'alert') {
      alertLogHeight.value = newHeight
    } else if (currentResizeType.value.startsWith('component-')) {
      const component = currentResizeType.value.replace('component-', '')
      componentLogHeights.value.set(component, newHeight)
    }
    
    // 性能优化：使用缓存的DOM元素引用，避免重复查询
    const resizingArea = resizeAreas.value.get(currentResizeType.value)
    if (resizingArea) {
      const handleElement = resizingArea.querySelector('.resize-handle') as HTMLElement
      if (handleElement) {
        const handleRect = handleElement.getBoundingClientRect()
        const viewportHeight = window.innerHeight
        const scrollThreshold = 80 // 滚动阈值
        
        // 如果拖拽手柄接近或超出视窗底部，自动向下滚动
        if (handleRect.bottom > viewportHeight - scrollThreshold) {
          // 计算需要滚动的距离
          const scrollDelta = Math.max(30, handleRect.bottom - (viewportHeight - scrollThreshold))
          
          // 使用即时滚动，避免平滑滚动导致的拖拽延迟
          window.scrollBy({
            top: scrollDelta,
            behavior: 'instant'
          })
          
          // 更新鼠标位置，避免拖拽中断
          startY.value += scrollDelta
          
          // 性能优化：减少DOM操作，只在必要时扩展页面高度
          const currentBodyHeight = document.body.scrollHeight
          const newBodyHeight = Math.max(currentBodyHeight, window.innerHeight + scrollDelta + 200)
          if (document.body.style.minHeight !== `${newBodyHeight}px`) {
            document.body.style.minHeight = `${newBodyHeight}px`
          }
          
          // 确保页面可以继续向下滚动
          if (document.documentElement.scrollHeight < newBodyHeight) {
            document.documentElement.style.minHeight = `${newBodyHeight}px`
          }
        }
      }
    }
    
    // 性能优化：在拖拽过程中保护其他面板的滚动位置
    // 特别是框架日志，防止在拖拽操作日志时自动跳转到底部
    if (currentResizeType.value === 'operation') {
      // 拖拽操作日志时，保护框架日志的滚动位置
      const frameworkContainer = document.querySelector('.framework-logs') as HTMLElement
      if (frameworkContainer) {
        const savedPosition = scrollPositionMemory.value.get('framework')
        if (savedPosition !== undefined && Math.abs(frameworkContainer.scrollTop - savedPosition) > 5) {
          frameworkContainer.scrollTop = savedPosition
          // 强制保持滚动位置，防止虚拟列表重新渲染时跳转
          setTimeout(() => {
            if (frameworkContainer.scrollTop !== savedPosition) {
              frameworkContainer.scrollTop = savedPosition
            }
          }, 0)
        }
      }
    } else if (currentResizeType.value === 'framework') {
      // 拖拽框架日志时，保护操作日志的滚动位置
      const operationContainer = document.querySelector('.operation-logs') as HTMLElement
      if (operationContainer) {
        const savedPosition = scrollPositionMemory.value.get('operation')
        if (savedPosition !== undefined && Math.abs(operationContainer.scrollTop - savedPosition) > 5) {
          operationContainer.scrollTop = savedPosition
          // 强制保持滚动位置，防止虚拟列表重新渲染时跳转
          setTimeout(() => {
            if (operationContainer.scrollTop !== savedPosition) {
              operationContainer.scrollTop = savedPosition
            }
          }, 0)
        }
      }
    }
    
    // 额外保护：定期检查并恢复所有面板的滚动位置
    if (currentResizeType.value) {
      setTimeout(() => {
        ['operation', 'framework', 'alert'].forEach(type => {
          if (type !== currentResizeType.value) {
            const container = document.querySelector(`.${type}-logs`) as HTMLElement
            if (container) {
              const savedPosition = scrollPositionMemory.value.get(type)
              if (savedPosition !== undefined && Math.abs(container.scrollTop - savedPosition) > 5) {
                container.scrollTop = savedPosition
              }
            }
          }
        })
      }, 5)
    }
  })
}

const stopResize = () => {
  isResizing.value = false
  currentResizeType.value = ''
  
  // 性能优化：使用缓存的DOM元素引用，避免重复查询
  resizeAreas.value.forEach((area, type) => {
    area.classList.remove('resizing')
    // 清理自动滚动相关的数据属性
    area.removeAttribute('data-initial-scroll')
    area.removeAttribute('data-initial-bottom')
  })
  
  // 清理缓存的DOM引用
  resizeAreas.value.clear()
  
  // 清理动态设置的body高度
  document.body.style.minHeight = ''
  
  // 性能优化：拖拽结束后使用防抖恢复滚动位置
  if (pendingResizeUpdate.value) {
    debouncedRestoreScrollPositions()
    pendingResizeUpdate.value = false
  }
  
  // 重置节流计时器
  resizeUpdateThrottle.value = 0
  
  document.removeEventListener('mousemove', handleResizeDrag)
  document.removeEventListener('mouseup', stopResize)
}

// 保存所有面板的滚动位置
const saveAllScrollPositions = () => {
  // 性能优化：批量保存滚动位置，减少DOM查询
  const containers = [
    { selector: '.operation-logs', key: 'operation' },
    { selector: '.framework-logs', key: 'framework' },
    { selector: '.alert-log-table-wrapper', key: 'alert' }
  ]
  
  containers.forEach(({ selector, key }) => {
    const container = document.querySelector(selector) as HTMLElement
    if (container) {
      scrollPositionMemory.value.set(key, container.scrollTop)
    }
  })
  
  // 保存所有调试日志面板的滚动位置
  selectedComponents.value.forEach(component => {
    const componentContainer = document.querySelector(`.component-logs[data-component="${component}"]`) as HTMLElement
    if (componentContainer) {
      scrollPositionMemory.value.set(`component-${component}`, componentContainer.scrollTop)
    }
  })
}

// 恢复所有面板的滚动位置
const restoreAllScrollPositions = () => {
  // 性能优化：批量恢复滚动位置，减少DOM查询
  const containers = [
    { selector: '.operation-logs', key: 'operation' },
    { selector: '.framework-logs', key: 'framework' },
    { selector: '.alert-log-table-wrapper', key: 'alert' }
  ]
  
  containers.forEach(({ selector, key }) => {
    const container = document.querySelector(selector) as HTMLElement
    if (container) {
      const savedPosition = scrollPositionMemory.value.get(key)
      if (savedPosition !== undefined) {
        // 确保滚动位置在有效范围内
        const maxScrollTop = container.scrollHeight - container.clientHeight
        const targetPosition = Math.min(savedPosition, maxScrollTop)
        if (container.scrollTop !== targetPosition) {
          container.scrollTop = targetPosition
        }
      }
    }
  })
  
  // 恢复所有调试日志面板的滚动位置
  selectedComponents.value.forEach(component => {
    const componentContainer = document.querySelector(`.component-logs[data-component="${component}"]`) as HTMLElement
    if (componentContainer) {
      const savedPosition = scrollPositionMemory.value.get(`component-${component}`) || 0
      if (componentContainer.scrollTop !== savedPosition) {
        componentContainer.scrollTop = savedPosition
      }
    }
  })
}

// 性能优化：防抖的滚动位置恢复
const debouncedRestoreScrollPositions = debounce(restoreAllScrollPositions, 100)

// 获取组件日志高度
const getComponentLogHeight = (component: string) => {
  return componentLogHeights.value.get(component) || Math.floor(window.innerHeight / 3)
}

// 计算属性
const currentWorkspace = computed(() => workspaceStore.currentWorkspace)
const hasWorkspaceData = computed(() => {
  if (!currentWorkspace.value) return false
  return fileDataStore.hasFileData(currentWorkspace.value.id)
})

// MDB 高亮内容计算属性
const highlightedMdbContent = computed(() => {
  if (!mdbDrawerContent.value) return ''
  
  const searchTerm = mdbSearchText.value.trim()
  if (!searchTerm) {
    return mdbDrawerContent.value
  }
  
  // 转义 HTML 特殊字符
  const escapeHtml = (text: string) => {
    return text
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;')
      .replace(/'/g, '&#39;')
  }
  
  const escapedContent = escapeHtml(mdbDrawerContent.value)
  const escapedSearchTerm = escapeHtml(searchTerm)
  
  // 创建正则表达式，不区分大小写
  const regex = new RegExp(`(${escapedSearchTerm})`, 'gi')
  
  // 替换匹配项为高亮标记
  return escapedContent.replace(regex, '<mark class="search-highlight">$1</mark>')
})

// CSR 高亮内容计算属性
const highlightedCSRContent = computed(() => {
  if (!mdbDrawerContent.value || currentMdbComponent.value !== 'BMC') {
    return []
  }
  
  const searchTerm = mdbSearchText.value.trim()
  
  const parsedContent = parseCSRContent(mdbDrawerContent.value)
  
  if (!searchTerm) {
    // 没有搜索词时，直接返回原始内容
    return parsedContent.map(item => ({
      ...item,
      highlightedText: item.text
    }))
  }
  
  // 转义 HTML 特殊字符
  const escapeHtml = (text: string) => {
    return text
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;')
      .replace(/'/g, '&#39;')
  }
  
  const escapedSearchTerm = escapeHtml(searchTerm)
  
  // 创建正则表达式，不区分大小写
  const regex = new RegExp(`(${escapedSearchTerm})`, 'gi')
  
  // 为每个项目添加高亮文本
  return parsedContent.map(item => {
    const escapedText = escapeHtml(item.text)
    const highlightedText = escapedText.replace(regex, '<mark class="search-highlight">$1</mark>')
    return {
      ...item,
      highlightedText
    }
  })
})

// MDB 高亮内容行计算属性
const highlightedMdbContentLines = computed(() => {
  if (!mdbDrawerContent.value || currentMdbComponent.value === 'BMC') {
    return []
  }
  
  const searchTerm = mdbSearchText.value.trim()
  if (!searchTerm) {
    // 没有搜索词时，按行分割内容
    return mdbDrawerContent.value.split('\n')
  }
  
  // 转义 HTML 特殊字符
  const escapeHtml = (text: string) => {
    return text
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;')
      .replace(/'/g, '&#39;')
  }
  
  const escapedSearchTerm = escapeHtml(searchTerm)
  const regex = new RegExp(`(${escapedSearchTerm})`, 'gi')
  
  // 按行分割并高亮每行
  return mdbDrawerContent.value.split('\n').map(line => {
    const escapedLine = escapeHtml(line)
    return escapedLine.replace(regex, '<mark class="search-highlight">$1</mark>')
  })
})

// 检查MDB内容行是否为当前匹配
function isCurrentMdbMatch(lineIndex: number): boolean {
  if (currentMatchIndex.value < 0 || currentMatchIndex.value >= searchMatches.value.length) {
    return false
  }
  
  // 对于MDB内容，searchMatches存储的是字符位置，需要转换为行索引
  const matchCharIndex = searchMatches.value[currentMatchIndex.value]
  const lines = mdbDrawerContent.value.split('\n')
  let currentCharIndex = 0
  
  for (let i = 0; i < lines.length; i++) {
    if (currentCharIndex <= matchCharIndex && matchCharIndex < currentCharIndex + lines[i].length + 1) {
      return i === lineIndex
    }
    currentCharIndex += lines[i].length + 1 // +1 for newline
  }
  
  return false
}

// APP版本高亮内容计算属性
const highlightedAppVersionContent = computed(() => {
  if (!appVersionContent.value) return ''
  
  const searchTerm = appVersionSearchText.value.trim()
  if (!searchTerm) {
    return appVersionContent.value
  }
  
  // 转义 HTML 特殊字符
  const escapeHtml = (text: string) => {
    return text
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;')
      .replace(/'/g, '&#39;')
  }
  
  const escapedContent = escapeHtml(appVersionContent.value)
  const escapedSearchTerm = escapeHtml(searchTerm)
  
  // 创建正则表达式，不区分大小写
  const regex = new RegExp(`(${escapedSearchTerm})`, 'gi')
  
  // 替换匹配项为高亮标记
  return escapedContent.replace(regex, '<mark class="app-version-search-highlight">$1</mark>')
})

// APP版本高亮内容行计算属性
const highlightedAppVersionContentLines = computed(() => {
  if (!appVersionContent.value) {
    return []
  }
  
  const searchTerm = appVersionSearchText.value.trim()
  if (!searchTerm) {
    // 没有搜索词时，按行分割内容
    return appVersionContent.value.split('\n')
  }
  
  // 转义 HTML 特殊字符
  const escapeHtml = (text: string) => {
    return text
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;')
      .replace(/'/g, '&#39;')
  }
  
  const escapedSearchTerm = escapeHtml(searchTerm)
  const regex = new RegExp(`(${escapedSearchTerm})`, 'gi')
  
  // 按行分割并高亮每行
  return appVersionContent.value.split('\n').map(line => {
    const escapedLine = escapeHtml(line)
    return escapedLine.replace(regex, '<mark class="app-version-search-highlight">$1</mark>')
  })
})

// 检查APP版本内容行是否为当前匹配
function isCurrentAppVersionMatch(lineIndex: number): boolean {
  if (appVersionCurrentMatchIndex.value < 0 || appVersionCurrentMatchIndex.value >= appVersionSearchMatches.value.length) {
    return false
  }
  
  // 对于APP版本内容，searchMatches存储的是字符位置，需要转换为行索引
  const matchCharIndex = appVersionSearchMatches.value[appVersionCurrentMatchIndex.value]
  const lines = appVersionContent.value.split('\n')
  let currentCharIndex = 0
  
  for (let i = 0; i < lines.length; i++) {
    if (currentCharIndex <= matchCharIndex && matchCharIndex < currentCharIndex + lines[i].length + 1) {
      return i === lineIndex
    }
    currentCharIndex += lines[i].length + 1 // +1 for newline
  }
  
  return false
}

// 调试日志组件搜索相关变量
const debugLogSearchText = ref<string>('')
const debouncedDebugLogSearchText = ref<string>('')

// 组件选项 - 添加缓存
const componentOptionsCache = ref<{ [workspaceId: string]: any[] }>({})
const componentOptions = computed(() => {
  if (!currentWorkspace.value) {
    console.log('componentOptions: 没有当前工作区')
    return []
  }
  
  const workspaceId = currentWorkspace.value.id
  
  // 检查缓存
  if (componentOptionsCache.value[workspaceId]) {
    return componentOptionsCache.value[workspaceId]
  }
  
  // 优先使用 hasWorkspaceData 作为响应式指示器，但也尝试直接获取数据作为容错
  if (!hasWorkspaceData.value) {
    const fileData = fileDataStore.getFileData(workspaceId)
    if (!fileData) {
      return []
    }
  }
  
  const fileData = fileDataStore.getFileData(workspaceId)
  
  if (!fileData) {
    return []
  }
  
  // 从 dump_info/AppDump 文件夹下遍历获取组件名称
  const components = extractComponentsFromAppDump(fileData)
  
  const options = components.map(component => ({
    label: component,
    value: component
  }))
  
  // 按字母顺序排序
  options.sort((a, b) => a.label.localeCompare(b.label, 'zh-CN', { numeric: true }))
  
  // 缓存结果
  componentOptionsCache.value[workspaceId] = options
  
  return options
})

// 过滤后的组件选项（根据搜索文本）
const filteredComponentOptions = computed(() => {
  const options = componentOptions.value
  if (!options || options.length === 0) {
    return []
  }
  
  // 根据搜索文本过滤组件
  if (debouncedDebugLogSearchText.value && debouncedDebugLogSearchText.value.trim() !== '') {
    const searchText = debouncedDebugLogSearchText.value.toLowerCase().trim()
    const filteredOptions = options.filter(option => 
      option.label.toLowerCase().includes(searchText) || 
      option.value.toLowerCase().includes(searchText)
    )
    // 确保过滤后的结果也按字母顺序排序
    return filteredOptions.sort((a, b) => a.label.localeCompare(b.label, 'zh-CN', { numeric: true }))
  }
  
  return options
})

// 右键上下文菜单选项
const contextMenuOptions = computed(() => {
  // 检查当前选中的日志项时间是否为1970-01-01
  if (contextMenuLogItem.value && contextMenuLogItem.value.timestamp) {
    const logDate = new Date(contextMenuLogItem.value.timestamp)
    
    // 检查是否为1970-01-01（Unix时间戳0）
    if (logDate.getFullYear() === 1970 && logDate.getMonth() === 0 && logDate.getDate() === 1) {
      return [] // 返回空数组，不显示任何菜单项
    }
  }
  
  return [
    {
      label: '分析时间范围',
      key: 'analyze-time-range',
      icon: () => h('span', { style: 'margin-right: 8px;' }, '⏰')
    }
  ]
})

// 过滤后的告警日志
const filteredAlertLogs = computed(() => {
  let filtered = alertLogs.value
  
  // 应用全局搜索文本过滤
  if (debouncedSearchText.value && debouncedSearchText.value.trim() !== '') {
    const globalSearchText = debouncedSearchText.value.toLowerCase().trim()
    filtered = filtered.filter(log => {
      return Object.values(log).some(value => 
        String(value).toLowerCase().includes(globalSearchText)
      )
    })
  }
  
  // 应用排除关键词过滤
  if (excludeKeywords.value.length > 0) {
    filtered = filtered.filter(log => {
      const logContent = Object.values(log).join(' ').toLowerCase()
      return !excludeKeywords.value.some(excludeKeyword => 
        excludeKeyword.trim() && logContent.includes(excludeKeyword.toLowerCase().trim())
      )
    })
  }
  
  // 应用告警日志专用搜索文本过滤
  if (alertLogSearchText.value) {
    const searchText = alertLogSearchText.value.toLowerCase()
    filtered = filtered.filter(log => {
      return Object.values(log).some(value => 
        String(value).toLowerCase().includes(searchText)
      )
    })
  }
  
  // 应用时间过滤
  if (startTime.value || endTime.value) {
    filtered = filtered.filter(log => {
      // 查找Generation Time列的键
      const generationTimeColumn = alertLogColumns.value.find(col => col.title === 'Generation Time')
      if (!generationTimeColumn) return true
      
      const logTimestamp = log[generationTimeColumn.key]
      if (!logTimestamp) return true
      
      try {
        const logTime = new Date(logTimestamp).getTime()
        
        if (startTime.value && logTime < startTime.value) {
          return false
        }
        
        if (endTime.value && logTime > endTime.value) {
          return false
        }
        
        return true
      } catch (error) {
        console.warn('解析告警日志时间失败:', logTimestamp, error)
        return true // 解析失败时保留该条日志
      }
    })
  }
  
  // 应用列头筛选
  alertLogColumns.value.forEach(column => {
    if (column.filterOptionValues && column.filterOptionValues.length > 0) {
      filtered = filtered.filter(row => {
        const cellValue = row[column.key]
        return column.filterOptionValues.includes(cellValue)
      })
    }
  })
  
  return filtered
})

// 滚动控制菜单选项
const scrollControlMenuOptions = computed(() => {
  const componentName = scrollControlMenuTarget.value
  if (!componentName) return []
  
  const currentAutoScroll = autoScrollToBottom.value.get(componentName) || false
  const currentPosition = scrollPositionMemory.value.get(componentName) || 0
  
  return [
    {
      label: '滚动到顶部',
      key: 'scroll-to-top',
      icon: () => h('span', { style: 'margin-right: 8px;' }, '⬆️')
    },
    {
      label: '滚动到底部',
      key: 'scroll-to-bottom',
      icon: () => h('span', { style: 'margin-right: 8px;' }, '⬇️')
    },
    {
      label: '恢复位置',
      key: 'restore-position',
      icon: () => h('span', { style: 'margin-right: 8px;' }, '🔄'),
      disabled: currentPosition === 0
    },
    {
      label: currentAutoScroll ? '关闭自动滚动' : '开启自动滚动',
      key: 'toggle-auto-scroll',
      icon: () => h('span', { style: 'margin-right: 8px;' }, currentAutoScroll ? '⏹️' : '▶️')
    },
    {
      label: `当前位置: ${Math.round(currentPosition)}px`,
      key: 'position-info',
      disabled: true
    }
  ]
})

// 统计表格列定义 - 需要为每个组件动态生成
function getStatsColumns(component: string) {
  return [
    {
      title: 'Level',
      key: 'level',
      width: 80
    },
    {
      title: 'Total',
      key: 'total',
      width: 80
    }
  ]
}

// 方法
function toggleComponent(component: string, checked: boolean) {
  
  let newComponents: string[]
  if (checked) {
    // 添加组件
    if (!selectedComponents.value.includes(component)) {
      newComponents = [...selectedComponents.value, component]
    } else {
      newComponents = selectedComponents.value
    }
  } else {
    // 移除组件
    newComponents = selectedComponents.value.filter(c => c !== component)
  }
  
  // 调用原有的组件变更逻辑
  onComponentsChange(newComponents)
}

function onComponentsChange(components: string[]) {
  // 确保 selectedComponents.value 是数组
  const currentComponents = Array.isArray(selectedComponents.value) 
    ? selectedComponents.value 
    : Object.values(selectedComponents.value)
  
  // 使用 Set 提高查找效率
  const newComponentsSet = new Set(components)
  const currentComponentsSet = new Set(currentComponents)
  
  // 计算被移除的组件（基于旧的选择）
  const removedComponents = currentComponents.filter(comp => !newComponentsSet.has(comp as string))
  
  // 清理被移除的组件
  removedComponents.forEach(comp => {
    const component = comp as string
    componentLogs.value.delete(component)
    // 清理组件搜索数据
    componentSearchTexts.value.delete(component)
    debouncedComponentSearchTexts.value.delete(component)
    // 清理该组件的缓存
    const cacheKeysToDelete: string[] = []
    filteredLogsCache.value.forEach((_, key) => {
      if (key.startsWith(`${component}_`)) {
        cacheKeysToDelete.push(key)
      }
    })
    cacheKeysToDelete.forEach(key => filteredLogsCache.value.delete(key))
    
    // 重置被移除组件的日志区域高度
    componentLogHeights.value.delete(component)
  })
  
  // 更新 selectedComponents，让其他地方能正确获取当前选择
  selectedComponents.value = components
  
  // 异步加载新选中组件的日志，避免阻塞UI
  const newComponents = components.filter(component => !currentComponentsSet.has(component))
  
  // 强制处理所有选中的组件，确保转储数据被加载
  if (components.length > 0) {
    // 使用 setTimeout 让UI先响应
    setTimeout(() => {
      components.forEach(async (component) => {
        // 强制清除调试日志缓存，确保重新加载转储数据
        const workspaceId = currentWorkspace.value?.id
        if (workspaceId && runtimeLogCache.value[workspaceId]) {
          delete runtimeLogCache.value[workspaceId]
        }
        
        // 强制重新加载组件日志，确保转储数据被包含
        
        // 为组件初始化级别过滤（默认启用所有级别）
        if (!levelFilters.value.has(component)) {
        }
        
        await loadComponentLogs(component)
        
        // 只为新组件初始化日志区域高度，保持已有组件的高度不变
        nextTick(() => {
          setTimeout(() => {
            // 如果组件还没有设置高度，才设置初始高度
            if (!componentLogHeights.value.has(component)) {
              const componentHeight = Math.floor(window.innerHeight / 3)
              componentLogHeights.value.set(component, componentHeight)
            }
          }, 100)
        })
      })
    }, 0)
    
    // 如果有新增的组件，自动聚焦到最后一个新增的组件面板
    if (newComponents.length > 0) {
      const lastNewComponent = newComponents[newComponents.length - 1]
      setTimeout(() => {
        scrollToLogPanel('component', lastNewComponent)
      }, 300) // 延迟300ms确保DOM完全渲染和高度设置完成
    }
  } else {
    console.log('没有选中的组件')
  }
  
  // 重置新组件的滚动位置状态
  nextTick(() => {
    components.forEach((component, index) => {
      const componentName = `component-${index}`
      userScrolled.value.set(componentName, false)
      scrollPositionMemory.value.delete(componentName)
      
      // 强制虚拟列表重新渲染，彻底解决滚动条消失问题
      const currentKey = componentVirtualListKeys.value.get(component) || 0
      componentVirtualListKeys.value.set(component, currentKey + 1)
    })
  })
  
}

// 时间变化防抖处理
let timeChangeDebounceTimer: ReturnType<typeof setTimeout> | null = null

function onTimeChange() {
  
  // 清理之前的防抖定时器
  if (timeChangeDebounceTimer) {
    clearTimeout(timeChangeDebounceTimer)
  }
  
  // 防抖处理，500ms后再执行实际的更新操作
  timeChangeDebounceTimer = setTimeout(() => {
    performTimeChangeUpdate()
  }, 500)
}

function performTimeChangeUpdate() {
  // 清理所有缓存，因为时间变化会影响所有过滤结果
  filteredLogsCache.value.clear()
  
  // 重置所有滚动位置状态，避免时间过滤后滚动位置异常
  nextTick(() => {
    // 重置框架日志滚动位置
    userScrolled.value.set('framework', false)
    scrollPositionMemory.value.delete('framework')
    safeUpdateFrameworkVirtualList()
    
    // 重置操作日志滚动位置
    userScrolled.value.set('operation', false)
    scrollPositionMemory.value.delete('operation')
    safeUpdateOperationVirtualList()
    
    // 重置组件日志滚动位置
    selectedComponents.value.forEach((component) => {
      const componentName = `component-${selectedComponents.value.indexOf(component)}`
      userScrolled.value.set(componentName, false)
      scrollPositionMemory.value.delete(componentName)
      
      const currentKey = componentVirtualListKeys.value.get(component) || 0
      componentVirtualListKeys.value.set(component, currentKey + 1)
    })
    
    // 更新区域筛选状态
    updateAreaFilterStatus()
    
    // 更新操作日志Interface选项，确保与当前时间筛选条件一致
    if (operationLogEnabled.value) {
      updateOperationInterfaceOptions()
    }
  })
  
  // 异步重新加载所有选中组件的日志，避免阻塞UI
  setTimeout(() => {
    selectedComponents.value.forEach(async (component) => {
      await loadComponentLogs(component)
      
      // 只为新组件初始化日志区域高度，保持已有组件的高度不变
      nextTick(() => {
        setTimeout(() => {
          // 如果组件还没有设置高度，才设置初始高度
          if (!componentLogHeights.value.has(component)) {
            const componentHeight = Math.floor(window.innerHeight / 3)
            componentLogHeights.value.set(component, componentHeight)
          }
        }, 100)
      })
    })
  }, 0)
}

function onSearchChange() {
  // 搜索框变化时不需要重新加载，只需要更新防抖后的搜索文本
  // 实际的过滤会在 getFilteredComponentLogs 中实时进行
}

// 排除关键词相关函数
function onExcludeSearchInputChange() {
  // 排除关键词输入框变化时的处理
}

function addExcludeKeyword() {
  const keyword = excludeSearchInput.value.trim()
  if (keyword && !excludeKeywords.value.includes(keyword)) {
    excludeKeywords.value.push(keyword)
    excludeSearchInput.value = '' // 清空输入框
    // 触发过滤更新
    updateAreaFilterStatus()
    // 更新操作日志Interface选项，确保与当前排除关键词条件一致
    updateOperationInterfaceOptions()
  }
}

function removeExcludeKeyword(index: number) {
  excludeKeywords.value.splice(index, 1)
  // 触发过滤更新
  updateAreaFilterStatus()
  // 更新操作日志Interface选项，确保与当前排除关键词条件一致
  updateOperationInterfaceOptions()
}

function onComponentSearchChange(component: string, value: string) {
  // 更新组件的搜索文本
  componentSearchTexts.value.set(component, value)
}

function onDebugLogSearchChange(value: string) {
  // 更新调试日志组件搜索文本
  debugLogSearchText.value = value
}

function showComponentInfo(component: string) {
  // 显示组件资源协作接口信息
  
  if (!currentWorkspace.value) {
    return
  }
  
  const fileData = fileDataStore.getFileData(currentWorkspace.value.id)
  
  if (!fileData) {
    return
  }
  
  // 构建 mdb_info.log 文件路径
  const mdbInfoPath = `dump_info/AppDump/${component}/mdb_info.log`
  
  try {
    const mdbContent = extractFileContent(fileData, mdbInfoPath)
    if (mdbContent) {
      // 这里可以进一步处理文件内容，比如解析或格式化
      return mdbContent
    } else {
      return `未找到组件 ${component} 的 MDB 信息文件`
    }
  } catch (error) {
    return `读取组件 ${component} 的 MDB 信息失败`
  }
}

function showCSRInfo(): string {
  // 显示CSR加载信息
  
  if (!currentWorkspace.value) {
    return '没有工作区'
  }
  
  const fileData = fileDataStore.getFileData(currentWorkspace.value.id)
  
  if (!fileData) {
    return '未找到文件数据'
  }
  
  // 构建 connectors.txt 文件路径
  const csrInfoPath = 'dump_info/AppDump/hwdiscovery/connectors.txt'
  
  try {
    const csrContent = extractFileContent(fileData, csrInfoPath)
    if (csrContent) {
      return csrContent
    } else {
      return '未找到CSR信息文件'
    }
  } catch (error) {
    return '读取CSR信息失败'
  }
}

// 解析CSR内容为结构化数据
// 解析连接器属性，提取Presence值
function parseConnectorProperties(content: string): Map<string, number> {
  const lines = content.split('\n')
  const connectorPresenceMap = new Map<string, number>()
  
  let currentConnector = ''
  
  for (let i = 0; i < lines.length; i++) {
    const line = lines[i].trim()
    
    // 检查是否是连接器定义行（以Connector_开头且包含冒号）
    if (line.includes('Connector_') && line.includes(':')) {
      // 提取连接器名称
      const connectorMatch = line.match(/(Connector_[^:]+):/)
      if (connectorMatch) {
        currentConnector = connectorMatch[1].trim()
      }
    }
    // 检查是否是Presence属性行
    else if (currentConnector && (line.startsWith('.Presence') || line.startsWith('-Presence'))) {
      const presenceMatch = line.match(/\.Presence\s*:\s*(\d+)/)
      if (presenceMatch) {
        const presenceValue = parseInt(presenceMatch[1], 10)
        connectorPresenceMap.set(currentConnector, presenceValue)
      }
    }
    // 如果遇到新的连接器定义或空行，重置当前连接器
    else if (line === '' || (line.includes('Connector_') && line.includes(':'))) {
      // 空行或新连接器定义时，准备处理下一个连接器
      if (line.includes('Connector_') && line.includes(':')) {
        const connectorMatch = line.match(/(Connector_[^:]+):/)
        if (connectorMatch) {
          currentConnector = connectorMatch[1].trim()
        }
      }
    }
  }
  
  return connectorPresenceMap
}

function parseCSRContent(content: string): Array<{text: string, level: number, hasChildren: boolean, connectorName: string, presence?: number}> {
  const lines = content.split('\n')
  const result: Array<{text: string, level: number, hasChildren: boolean, connectorName: string, presence?: number}> = []
  
  // 首先解析所有连接器的Presence属性
  const connectorPresenceMap = parseConnectorProperties(content)
  
  lines.forEach((line, lineIndex) => {
    if (line.trim()) {
      // 计算缩进级别
      const match = line.match(/^(\s*)(├──|└──)?(.+)$/)
      if (match) {
        const indent = match[1].length
        const level = Math.floor(indent / 2)
        const text = match[3] || line.trim()
        const hasChildren = line.includes('├──') || line.includes('└──')
        
        // 提取Connector名称 - 支持root节点和其他Connector_节点
        let connectorName = ''
        let presence: number | undefined = undefined
        
        if (text.includes('root')) {
          // root节点设置connectorName为'root'
          connectorName = 'root'
        } else if (text.includes('Connector_')) {
          // 其他Connector_节点，提取连接器名称
          // 清理所有前缀特殊字符：├──、└──、|、-、空格等
          connectorName = text.replace(/[├──└──|\-\s]+/g, '').trim()
          // 如果还有其他特殊字符，进一步清理
          connectorName = connectorName.replace(/^[^a-zA-Z0-9_]+/, '')
          
          // 获取该连接器的Presence值
          presence = connectorPresenceMap.get(connectorName)
        }
        
        result.push({ text, level, hasChildren, connectorName, presence })
      } else {
        // 对于不匹配格式的行，仍然添加到结果中，但使用默认值
        result.push({ 
          text: line.trim(), 
          level: 0, 
          hasChildren: false, 
          connectorName: '', 
          presence: undefined
        })
      }
    }
  })
  
  return result
}

// 查看Connector详细信息
async function viewConnectorDetail(connectorName: string) {
  
  if (!currentWorkspace.value) {
    console.error('没有工作区')
    return
  }

  const fileData = fileDataStore.getFileData(currentWorkspace.value.id)
  if (!fileData) {
    console.error('没有文件数据')
    return
  }

  // 构建平台相关的路径
  const basePath = buildPlatformPath('dump_info', 'AppDump')
  const hwdiscoveryPath = buildPlatformPath(basePath, 'hwdiscovery')
  
  console.log(`查找Connector: ${connectorName}`)
  console.log(`基础路径: ${basePath}`)
  console.log(`hwdiscovery路径: ${hwdiscoveryPath}`)

  // 尝试读取 .sr 文件
  let srPath: string
  if (connectorName === 'root') {
    // 对于root节点，直接加载固定的root.sr文件
    srPath = buildPlatformPath(hwdiscoveryPath, 'root.sr')
  } else {
    // 对于其他节点，使用动态文件名
    srPath = buildPlatformPath(hwdiscoveryPath, `${connectorName}.sr`)
  }
  
  console.log(`尝试读取.sr文件: ${srPath}`)
  
  try {
    const srContent = extractFileContent(fileData, srPath)
    if (srContent) {
      console.log(`成功读取.sr文件: ${srPath}`)
      currentConnectorDetail.value = srContent
      currentConnectorDetailFileName.value = `${connectorName}.sr`
      connectorDetailDrawerShow.value = true
      return
    } else {
      console.log(`未找到.sr文件: ${srPath}`)
    }
  } catch (error) {
    console.log(`读取.sr文件失败: ${srPath}`, error)
  }

  // 尝试读取 .bin 文件
  let binPath: string
  if (connectorName === 'root') {
    // 对于root节点，直接加载固定的root.bin文件
    binPath = buildPlatformPath(hwdiscoveryPath, 'root.bin')
  } else {
    // 对于其他节点，使用动态文件名
    binPath = buildPlatformPath(hwdiscoveryPath, `${connectorName}.bin`)
  }
  
  console.log(`尝试读取.bin文件: ${binPath}`)
  
  try {
    // 对于.bin文件，直接从IndexedDB获取原始Blob数据
    const { getArchiveEntry } = await import('../utils/archiveDb')
    const wsId = workspaceStore.currentWorkspace?.id
    if (!wsId) {
      console.error('没有当前工作区ID')
      return
    }
    
    const entry = await getArchiveEntry(wsId, binPath)
    console.log(`尝试读取.bin文件: ${binPath}`, entry ? '找到' : '未找到')
    
    if (entry && entry.content && entry.content instanceof Blob) {
      console.log(`找到.bin文件: ${binPath}，大小: ${entry.content.size} 字节`)
      
      // 显示不支持.bin格式的提示信息
      const notSupportedMessage = `暂不支持查看.bin格式的文件\n\n文件信息:\n- 文件名: ${connectorName}.bin\n- 文件大小: ${entry.content.size} 字节\n- 文件路径: ${binPath}\n\n请使用其他格式的文件查看CSR信息。`
      
      currentConnectorDetail.value = notSupportedMessage
      currentConnectorDetailFileName.value = `${connectorName}.bin`
      connectorDetailDrawerShow.value = true
      return
      
      // 注释掉的解压代码（保留以备后用）
      /*
      // 读取Blob为ArrayBuffer
      const arrayBuffer = await entry.content.arrayBuffer()
      const uint8Array = new Uint8Array(arrayBuffer)
      
      console.log(`二进制数据长度: ${uint8Array.length} 字节`)
      console.log(`前32字节:`, Array.from(uint8Array.slice(0, 32)).map(b => b.toString(16).padStart(2, '0')).join(' '))
      
      // 解压.bin文件并提取CSR数据
      try {
        const csrData = extractCSRFromBin(uint8Array)
        if (csrData) {
          console.log(`成功解析CSR数据，长度: ${csrData.length} 字符`)
          // 格式化JSON显示
          try {
            const parsed = JSON.parse(csrData)
            const formattedCSR = JSON.stringify(parsed, null, 2)
            currentConnectorDetail.value = formattedCSR
            currentConnectorDetailFileName.value = `${connectorName}.bin (CSR数据)`
            connectorDetailDrawerShow.value = true
            return
          } catch (jsonError) {
            console.log('CSR数据不是有效的JSON，显示原始内容')
            currentConnectorDetail.value = csrData
            currentConnectorDetailFileName.value = `${connectorName}.bin (CSR数据)`
            connectorDetailDrawerShow.value = true
            return
          }
        } else {
          console.log('CSR数据解析返回null')
        }
      } catch (csrError) {
        console.error('CSR数据解析失败:', csrError)
        // 如果CSR解析失败，显示原始二进制内容
        const displayContent = `二进制文件内容 (${connectorName}.bin):\n\n解析CSR数据失败: ${csrError}\n\n文件大小: ${uint8Array.length} 字节\n\n前100字节内容:\n${Array.from(uint8Array.slice(0, 100)).map(b => b.toString(16).padStart(2, '0')).join(' ')}`
        currentConnectorDetail.value = displayContent
        currentConnectorDetailFileName.value = `${connectorName}.bin`
        connectorDetailDrawerShow.value = true
        return
      }
      */
    } else {
      console.log(`未找到.bin文件或内容无效: ${binPath}`)
      if (entry) {
        console.log('entry对象:', entry)
      }
    }
  } catch (error) {
    console.error('.bin 文件读取失败:', error)
  }

  // 如果都找不到，尝试读取 .txt 文件作为备选
  let txtPath: string
  if (connectorName === 'root') {
    // 对于root节点，直接加载固定的root.txt文件
    txtPath = buildPlatformPath(hwdiscoveryPath, 'root.txt')
  } else {
    // 对于其他节点，使用动态文件名
    txtPath = buildPlatformPath(hwdiscoveryPath, `${connectorName}.txt`)
  }
  
  console.log(`尝试读取.txt文件: ${txtPath}`)
  
  try {
    const txtContent = extractFileContent(fileData, txtPath)
    if (txtContent) {
      console.log(`成功读取.txt文件: ${txtPath}`)
      currentConnectorDetail.value = txtContent
      currentConnectorDetailFileName.value = `${connectorName}.txt`
      connectorDetailDrawerShow.value = true
      return
    } else {
      console.log(`未找到.txt文件: ${txtPath}`)
    }
  } catch (error) {
    console.log(`读取.txt文件失败: ${txtPath}`, error)
  }

  // 尝试其他可能的路径格式
  const alternativePaths = [
    `${connectorName}.bin`,
    `${connectorName}.sr`,
    `${connectorName}.txt`,
    `hwdiscovery/${connectorName}.bin`,
    `hwdiscovery/${connectorName}.sr`,
    `hwdiscovery/${connectorName}.txt`,
    `AppDump/hwdiscovery/${connectorName}.bin`,
    `AppDump/hwdiscovery/${connectorName}.sr`,
    `AppDump/hwdiscovery/${connectorName}.txt`
  ]
  
  console.log('尝试替代路径:', alternativePaths)
  
  for (const altPath of alternativePaths) {
    try {
      const { getArchiveEntry } = await import('../utils/archiveDb')
      const wsId = workspaceStore.currentWorkspace?.id
      if (!wsId) continue
      
      const entry = await getArchiveEntry(wsId, altPath)
      if (entry && entry.content && entry.content instanceof Blob) {
        console.log(`在替代路径找到文件: ${altPath}`)
        
        if (altPath.endsWith('.bin')) {
          // 处理.bin文件
          const arrayBuffer = await entry.content.arrayBuffer()
          const uint8Array = new Uint8Array(arrayBuffer)
          
          try {
            const csrData = extractCSRFromBin(uint8Array)
            if (csrData) {
              try {
                const parsed = JSON.parse(csrData)
                const formattedCSR = JSON.stringify(parsed, null, 2)
                currentConnectorDetail.value = formattedCSR
                currentConnectorDetailFileName.value = `${altPath} (CSR数据)`
                connectorDetailDrawerShow.value = true
                return
              } catch (jsonError) {
                currentConnectorDetail.value = csrData
                currentConnectorDetailFileName.value = `${altPath} (CSR数据)`
                connectorDetailDrawerShow.value = true
                return
              }
            }
          } catch (csrError) {
            console.log(`CSR解析失败: ${altPath}`, csrError)
          }
        } else {
          // 处理其他类型文件
          const textContent = await entry.content.text()
          currentConnectorDetail.value = textContent
          currentConnectorDetailFileName.value = altPath
          connectorDetailDrawerShow.value = true
          return
        }
      }
    } catch (error) {
      console.log(`替代路径尝试失败: ${altPath}`, error)
    }
  }
  
  // 显示页面提示
  showPageNotification(`未找到 ${connectorName} 连接器挂载的CSR文件`, 'warning')
}

// 从.bin文件中提取CSR数据
function extractCSRFromBin(data: Uint8Array): string | null {
  try {
    const decoder = new TextDecoder()
    
    console.log(`开始解析CSR数据，文件大小: ${data.length} 字节`)
    
    // 检查数据长度
    if (data.length < 25) {
      throw new Error('文件太小，无法包含有效的CSR数据')
    }
    
    // 获取偏移量 (Python: get_offset(data, slice(21,23)))
    const csrOffset = (data[21] | (data[22] << 8)) * 8
    const sigOffset = (data[23] | (data[24] << 8)) * 8
    
    console.log(`解析的偏移量: CSR=${csrOffset}, SIG=${sigOffset}`)
    console.log(`字节21-22: ${data[21].toString(16).padStart(2, '0')} ${data[22].toString(16).padStart(2, '0')}`)
    console.log(`字节23-24: ${data[23].toString(16).padStart(2, '0')} ${data[24].toString(16).padStart(2, '0')}`)
    
    // 检查偏移量有效性
    if (csrOffset + 56 >= data.length || sigOffset > data.length || csrOffset >= sigOffset) {
      throw new Error(`无效的偏移量: CSR=${csrOffset}, SIG=${sigOffset}, 文件大小=${data.length}`)
    }
    
    // 提取压缩数据 (Python: data[csr_offset+56:sig_offset])
    const compressedData = data.slice(csrOffset + 56, sigOffset)
    console.log(`压缩数据大小: ${compressedData.length} 字节`)
    console.log(`压缩数据前16字节:`, Array.from(compressedData.slice(0, 16)).map(b => b.toString(16).padStart(2, '0')).join(' '))
    
    // 使用pako库进行gzip解压
    const decompressed = pako.inflate(compressedData)
    console.log(`解压后数据大小: ${decompressed.length} 字节`)
    
    const csrStr = decoder.decode(decompressed)
    console.log(`解码后CSR字符串长度: ${csrStr.length} 字符`)
    console.log(`CSR数据前100字符:`, csrStr.substring(0, 100))
    
    return csrStr
  } catch (error) {
    console.error('CSR数据解析失败:', error)
    return null
  }
}

// 显示页面提示
function showPageNotification(message: string, type: 'info' | 'warning' | 'success' | 'error' = 'info') {
  pageNotification.value = {
    show: true,
    message,
    type
  }
  
  // 3秒后自动隐藏
  setTimeout(() => {
    pageNotification.value.show = false
  }, 3000)
}

// 关闭Connector详细信息抽屉
function closeConnectorDetailDrawer() {
  connectorDetailDrawerShow.value = false
  currentConnectorDetail.value = ''
  currentConnectorDetailFileName.value = ''
  // 清空搜索状态
  connectorDetailSearchText.value = ''
  connectorDetailSearchMatches.value = []
  connectorDetailCurrentMatchIndex.value = -1
}

// Connector详细信息搜索相关函数
function onConnectorDetailSearchChange(value: string) {
  if (!value.trim()) {
    connectorDetailSearchMatches.value = []
    connectorDetailCurrentMatchIndex.value = -1
    return
  }
  
  updateConnectorDetailSearchMatches()
}

function onConnectorDetailSearchEnter() {
  if (connectorDetailSearchMatches.value.length > 0) {
    connectorDetailNextMatch()
  }
}

function updateConnectorDetailSearchMatches() {
  if (!connectorDetailSearchText.value.trim() || !currentConnectorDetail.value) {
    connectorDetailSearchMatches.value = []
    connectorDetailCurrentMatchIndex.value = -1
    return
  }

  const searchTerm = connectorDetailSearchText.value.trim()
  const content = currentConnectorDetail.value
  
  // 尝试解析JSON并格式化
  let formattedContent = ''
  try {
    const parsed = JSON.parse(content)
    formattedContent = JSON.stringify(parsed, null, 2)
  } catch (error) {
    formattedContent = content
  }
  
  const matches: number[] = []
  let index = formattedContent.indexOf(searchTerm)
  
  while (index !== -1) {
    matches.push(index)
    index = formattedContent.indexOf(searchTerm, index + 1)
  }
  
  connectorDetailSearchMatches.value = matches
  connectorDetailCurrentMatchIndex.value = matches.length > 0 ? 0 : -1
}

function connectorDetailNextMatch() {
  if (connectorDetailSearchMatches.value.length === 0) return
  
  connectorDetailCurrentMatchIndex.value = 
    (connectorDetailCurrentMatchIndex.value + 1) % connectorDetailSearchMatches.value.length
  scrollToConnectorDetailCurrentMatch()
}

function connectorDetailPrevMatch() {
  if (connectorDetailSearchMatches.value.length === 0) return
  
  connectorDetailCurrentMatchIndex.value = 
    connectorDetailCurrentMatchIndex.value <= 0 
      ? connectorDetailSearchMatches.value.length - 1 
      : connectorDetailCurrentMatchIndex.value - 1
  scrollToConnectorDetailCurrentMatch()
}

// 调试函数：检查DOM状态
function debugConnectorDetailDOM() {
  const contentElement = document.querySelector('.connector-detail-scrollable-content')
  const highlightLines = document.querySelectorAll('.search-highlight-line')
  const highlightElements = document.querySelectorAll('.search-highlight')
  
  if (highlightElements.length > 0) {
    console.log('第一个高亮元素文本:', highlightElements[0].textContent)
  }
}

function scrollToConnectorDetailCurrentMatch() {
  if (connectorDetailCurrentMatchIndex.value < 0 || 
      connectorDetailCurrentMatchIndex.value >= connectorDetailSearchMatches.value.length) {
    return
  }
  
  // 等待DOM更新完成，增加延迟确保高亮元素已渲染
  setTimeout(() => {
    // 先尝试查找行级高亮元素
    let highlightElements = document.querySelectorAll('.search-highlight-line .search-highlight')
    
    // 如果没有找到，尝试查找所有高亮元素
    if (highlightElements.length === 0) {
      highlightElements = document.querySelectorAll('.search-highlight')
    }
    
    if (highlightElements.length > 0) {
      const currentMatchIndex = connectorDetailCurrentMatchIndex.value
      const targetElement = highlightElements[currentMatchIndex] as HTMLElement
      
      if (targetElement) {
        // 滚动到目标元素
        targetElement.scrollIntoView({
          behavior: 'smooth',
          block: 'center'
        })
        
        // 添加当前匹配的高亮样式
        highlightElements.forEach(el => el.classList.remove('search-highlight-current'))
        targetElement.classList.add('search-highlight-current')
        
        // 同时高亮对应的行
        const parentLine = targetElement.closest('.search-highlight-line')
        if (parentLine) {
          document.querySelectorAll('.search-highlight-line').forEach(el => 
            el.classList.remove('search-highlight-current-line')
          )
          parentLine.classList.add('search-highlight-current-line')
        }
      }
    } else {
      // 尝试直接滚动到内容区域
      const contentElement = document.querySelector('.connector-detail-scrollable-content')
      if (contentElement) {
        contentElement.scrollTop = 0
      }
    }
  }, 200) // 增加延迟到200ms
}

function getMdbDrawerTitle(): string {
  // 根据当前组件类型返回相应的抽屉标题
  if (currentMdbComponent.value === 'BMC') {
    return 'CSR加载信息'
  } else {
    return `${currentMdbComponent.value} 组件资源协作接口信息`
  }
}

function getMdbSearchPlaceholder(): string {
  // 根据当前组件类型返回相应的搜索框占位符
  if (currentMdbComponent.value === 'BMC') {
    return '搜索 CSR 信息内容... (按回车搜索下一个)'
  } else {
    return '搜索 MDB 信息内容... (按回车搜索下一个)'
  }
}

function getMdbErrorDescription(): string {
  // 根据当前组件类型返回相应的错误描述
  if (currentMdbComponent.value === 'BMC') {
    return '未找到CSR信息'
  } else {
    return '未找到组件信息'
  }
}

function getMdbErrorExtraText(): string {
  // 根据当前组件类型返回相应的错误额外文本
  if (currentMdbComponent.value === 'BMC') {
    return '该文件可能没有CSR信息文件'
  } else {
    return '该组件可能没有 MDB 信息文件'
  }
}

function openMdbDrawer(component: string) {
  // 检查工作区状态
  if (!currentWorkspace.value) {
    alert('请先加载一个工作区！')
    return
  }
  
  // 设置当前组件
  currentMdbComponent.value = component
  
  // 显示抽屉
  mdbDrawerShow.value = true
  
  // 清空之前的内容
  mdbDrawerContent.value = ''
  
  // 开始加载
  mdbDrawerLoading.value = true
  
  // 异步加载 MDB 信息
  setTimeout(() => {
    let content: string | undefined
    
    // 如果是CSR按钮（BMC参数），读取connectors.txt文件
    if (component === 'BMC') {
      content = showCSRInfo()
    } else {
      // 其他组件读取MDB信息
      content = showComponentInfo(component)
    }
    
    mdbDrawerLoading.value = false
    if (content) {
      mdbDrawerContent.value = content
    }
  }, 100)
}

function closeMdbDrawer() {
  mdbDrawerShow.value = false
  mdbDrawerContent.value = ''
  currentMdbComponent.value = ''
  mdbSearchText.value = '' // 清空搜索文本
  searchMatches.value = [] // 清空搜索匹配
  currentMatchIndex.value = -1 // 重置当前匹配索引
  
  // 清除所有高亮状态
  clearAllHighlights()
}

function openBmcVersionDrawer() {
  // 显示抽屉
  bmcVersionDrawerShow.value = true
  
  // 清空之前的内容
  bmcVersionContent.value = ''
  
  // 开始加载
  bmcVersionLoading.value = true
  
  // 异步加载 BMC 版本信息
  setTimeout(() => {
    const content = showBmcVersionInfo()
    bmcVersionLoading.value = false
    if (content) {
      bmcVersionContent.value = content
    }
  }, 100)
}

function closeBmcVersionDrawer() {
  bmcVersionDrawerShow.value = false
  bmcVersionContent.value = ''
}

function showBmcVersionInfo(): string {
  // 显示BMC版本信息
  
  if (!currentWorkspace.value) {
    console.warn('没有工作区')
    return '没有工作区'
  }
  
  const fileData = fileDataStore.getFileData(currentWorkspace.value.id)
  
  if (!fileData) {
    return '没有文件数据'
  }
  
  // 构建 BMC 版本文件路径，注意Windows和Linux路径兼容
  const bmcVersionPath = 'dump_info/RTOSDump/versioninfo/app_revision.txt'
  
  try {
    const bmcContent = extractFileContent(fileData, bmcVersionPath)
    if (bmcContent) {
      return bmcContent
    } else {
      return '未找到BMC版本信息文件'
    }
  } catch (error) {
    return '读取BMC版本信息失败'
  }
}

function openAppVersionDrawer() {
  // 显示抽屉
  appVersionDrawerShow.value = true
  
  // 清空之前的内容和搜索状态
  appVersionContent.value = ''
  appVersionSearchText.value = '' // 清空搜索文本
  appVersionSearchMatches.value = [] // 清空搜索匹配
  appVersionCurrentMatchIndex.value = -1 // 重置当前匹配索引
  
  // 开始加载
  appVersionLoading.value = true
  
  // 异步加载 APP 版本信息
  setTimeout(async () => {
    try {
      const content = await showAppVersionInfo()
      appVersionLoading.value = false
      if (content) {
        appVersionContent.value = content
      }
    } catch (error) {
      console.error('加载APP版本信息失败:', error)
      appVersionLoading.value = false
      appVersionContent.value = '加载失败'
    }
  }, 100)
}

function closeAppVersionDrawer() {
  appVersionDrawerShow.value = false
  appVersionContent.value = ''
  appVersionSearchText.value = '' // 清空搜索文本
  appVersionSearchMatches.value = [] // 清空搜索匹配
  appVersionCurrentMatchIndex.value = -1 // 重置当前匹配索引
}

async function showAppVersionInfo(): Promise<string> {
  // 显示APP版本信息
  
  if (!currentWorkspace.value) {
    return '没有工作区'
  }
  
  try {
    // 使用dataLoaderService加载组件版本信息
    const { dataLoaderService } = await import('../services/dataLoaderService')
    const versionContent = await dataLoaderService.loadComponentVersionInfo(currentWorkspace.value.id)
    
    if (versionContent) {
      console.log('showAppVersionInfo - 成功获取版本信息，内容长度:', versionContent.length)
      return versionContent
    } else {
      console.warn('showAppVersionInfo - 未找到APP版本信息文件')
      return '未找到APP版本信息文件'
    }
  } catch (error) {
    console.error('showAppVersionInfo - 读取APP版本信息失败:', error)
    return '读取APP版本信息失败'
  }
}

// APP版本搜索相关方法
function onAppVersionSearchChange(value: string) {
  // APP版本搜索文本变化时的处理
  
  // 更新搜索匹配位置
  updateAppVersionSearchMatches()
  
  // 重置当前匹配索引
  appVersionCurrentMatchIndex.value = -1
  
  // 如果有匹配项，自动跳转到第一个
  if (appVersionSearchMatches.value.length > 0) {
    appVersionCurrentMatchIndex.value = 0
    // 延迟执行滚动，确保DOM已更新
    setTimeout(() => {
      scrollToCurrentAppVersionMatch()
    }, 50)
  }
}

function onAppVersionSearchEnter(event: KeyboardEvent) {
  // 按回车键搜索下一个
  event.preventDefault()
  
  if (appVersionSearchMatches.value.length === 0) {
    return
  }
  
  // 如果当前没有选中任何匹配项，跳转到第一个
  if (appVersionCurrentMatchIndex.value === -1) {
    appVersionCurrentMatchIndex.value = 0
    scrollToCurrentAppVersionMatch()
    return
  }
  
  // 跳转到下一个匹配项
  goToNextAppVersionMatch()
}

function updateAppVersionSearchMatches() {
  const searchTerm = appVersionSearchText.value.trim()
  if (!searchTerm || !appVersionContent.value) {
    appVersionSearchMatches.value = []
    return
  }
  
  const matches: number[] = []
  const regex = new RegExp(searchTerm, 'gi')
  let match
  
  while ((match = regex.exec(appVersionContent.value)) !== null) {
    matches.push(match.index)
  }
  
  appVersionSearchMatches.value = matches
}

function scrollToCurrentAppVersionMatch() {
  if (appVersionCurrentMatchIndex.value < 0 || appVersionCurrentMatchIndex.value >= appVersionSearchMatches.value.length) {
    return
  }
  
  const matchIndex = appVersionSearchMatches.value[appVersionCurrentMatchIndex.value]
  const contentElement = document.querySelector('.app-version-content-text')
  if (!contentElement) return
  
  // 等待DOM更新完成后再滚动
  setTimeout(() => {
    // 获取所有高亮元素
    const highlights = document.querySelectorAll('.app-version-search-highlight')
    if (appVersionCurrentMatchIndex.value >= 0 && appVersionCurrentMatchIndex.value < highlights.length) {
      const currentHighlight = highlights[appVersionCurrentMatchIndex.value] as HTMLElement
      
      // 滚动到当前高亮元素
      currentHighlight.scrollIntoView({
        behavior: 'smooth',
        block: 'center',
        inline: 'nearest'
      })
      
      // 高亮当前匹配项
      highlightCurrentAppVersionMatch()
    }
  }, 100)
}

function highlightCurrentAppVersionMatch() {
  // 移除之前的高亮
  const highlights = document.querySelectorAll('.app-version-search-highlight')
  highlights.forEach(el => el.classList.remove('app-version-search-highlight-current'))
  
  // 添加当前高亮
  if (appVersionCurrentMatchIndex.value >= 0 && appVersionCurrentMatchIndex.value < highlights.length) {
    highlights[appVersionCurrentMatchIndex.value].classList.add('app-version-search-highlight-current')
  }
}

function goToPreviousAppVersionMatch() {
  if (appVersionSearchMatches.value.length === 0) return
  
  appVersionCurrentMatchIndex.value = appVersionCurrentMatchIndex.value <= 0 
    ? appVersionSearchMatches.value.length - 1 
    : appVersionCurrentMatchIndex.value - 1
  
  scrollToCurrentAppVersionMatch()
}

function goToNextAppVersionMatch() {
  if (appVersionSearchMatches.value.length === 0) return
  
  appVersionCurrentMatchIndex.value = appVersionCurrentMatchIndex.value >= appVersionSearchMatches.value.length - 1 
    ? 0 
    : appVersionCurrentMatchIndex.value + 1
  
  scrollToCurrentAppVersionMatch()
}

function onMdbSearchChange(value: string) {
  // 如果搜索文本被清空，清除所有高亮状态
  if (!value.trim()) {
    clearAllHighlights()
    searchMatches.value = []
    currentMatchIndex.value = -1
    return
  }
  
  // 更新搜索匹配位置
  updateSearchMatches()
  
  // 重置当前匹配索引
  currentMatchIndex.value = -1
  
  // 如果有匹配项，自动跳转到第一个
  if (searchMatches.value.length > 0) {
    currentMatchIndex.value = 0
    // 延迟执行滚动，确保DOM已更新
    setTimeout(() => {
      scrollToCurrentMatch()
    }, 50)
  }
}

function onSearchEnter(event: KeyboardEvent) {
  // 按回车键搜索下一个
  event.preventDefault()
  
  if (searchMatches.value.length === 0) {
    return
  }
  
  // 如果当前没有选中任何匹配项，跳转到第一个
  if (currentMatchIndex.value === -1) {
    currentMatchIndex.value = 0
    scrollToCurrentMatch()
    return
  }
  
  // 跳转到下一个匹配项
  goToNextMatch()
}

function updateSearchMatches() {
  const searchTerm = mdbSearchText.value.trim()
  if (!searchTerm || !mdbDrawerContent.value) {
    searchMatches.value = []
    // 如果没有搜索词，清除所有高亮状态
    clearAllHighlights()
    return
  }
  
  const matches: number[] = []
  
  if (currentMdbComponent.value === 'BMC') {
    // CSR内容搜索：在解析后的内容中查找匹配项
    const parsedContent = parseCSRContent(mdbDrawerContent.value)
    const regex = new RegExp(searchTerm, 'gi')
    
    parsedContent.forEach((item, itemIndex) => {
      let match
      while ((match = regex.exec(item.text)) !== null) {
        matches.push(itemIndex) // 存储项目索引而不是字符位置
      }
      // 重置正则表达式的lastIndex，以便下次匹配
      regex.lastIndex = 0
    })
    
  } else {
    // 普通MDB内容搜索：在原始文本中查找匹配项
    const regex = new RegExp(searchTerm, 'gi')
    let match
    
    while ((match = regex.exec(mdbDrawerContent.value)) !== null) {
      matches.push(match.index)
    }
  }
  
  searchMatches.value = matches
}

function scrollToCurrentMatch() {
  if (currentMatchIndex.value < 0 || currentMatchIndex.value >= searchMatches.value.length) {
    return
  }
  
  const matchIndex = searchMatches.value[currentMatchIndex.value]
  
  if (currentMdbComponent.value === 'BMC') {
    // CSR内容滚动：滚动到对应的文本行
    const csrLines = document.querySelectorAll('.csr-text-line')
    if (matchIndex >= 0 && matchIndex < csrLines.length) {
      const targetLine = csrLines[matchIndex] as HTMLElement
      
      // 滚动到目标行
      targetLine.scrollIntoView({
        behavior: 'smooth',
        block: 'center',
        inline: 'nearest'
      })
      
      // 高亮当前匹配项
      highlightCurrentMatch()
    }
  } else {
    // 普通MDB内容滚动：滚动到高亮元素
    const contentElement = document.querySelector('.mdb-content-text')
    if (!contentElement) return
    
    // 等待DOM更新完成后再滚动
    setTimeout(() => {
      // 获取所有高亮元素
      const highlights = document.querySelectorAll('.search-highlight')
      if (currentMatchIndex.value >= 0 && currentMatchIndex.value < highlights.length) {
        const currentHighlight = highlights[currentMatchIndex.value] as HTMLElement
        
        // 滚动到当前高亮元素
        currentHighlight.scrollIntoView({
          behavior: 'smooth',
          block: 'center',
          inline: 'nearest'
        })
        
        // 高亮当前匹配项
        highlightCurrentMatch()
      }
    }, 100)
  }
}

function clearAllHighlights() {
  if (currentMdbComponent.value === 'BMC') {
    // 清除CSR内容的所有高亮状态
    const csrLines = document.querySelectorAll('.csr-text-line')
    csrLines.forEach(line => {
      line.classList.remove('search-highlight-current')
    })
  } else {
    // 清除普通MDB内容的所有高亮状态
    const highlights = document.querySelectorAll('.search-highlight')
    highlights.forEach(el => {
      el.classList.remove('search-highlight-current')
    })
  }
}

function highlightCurrentMatch() {
  if (currentMdbComponent.value === 'BMC') {
    // CSR内容高亮：高亮当前匹配的行
    const csrLines = document.querySelectorAll('.csr-text-line')
    csrLines.forEach((line, index) => {
      line.classList.remove('search-highlight-current')
    })
    
    // 添加当前高亮：使用searchMatches中存储的实际匹配索引
    if (currentMatchIndex.value >= 0 && currentMatchIndex.value < searchMatches.value.length) {
      const actualMatchIndex = searchMatches.value[currentMatchIndex.value]
      
      if (actualMatchIndex >= 0 && actualMatchIndex < csrLines.length) {
        const targetLine = csrLines[actualMatchIndex] as HTMLElement
        targetLine.classList.add('search-highlight-current')
      }
    }
  } else {
    // 普通MDB内容高亮：高亮当前匹配的高亮元素
    const highlights = document.querySelectorAll('.search-highlight')
    highlights.forEach(el => el.classList.remove('search-highlight-current'))
    
    // 添加当前高亮
    if (currentMatchIndex.value >= 0 && currentMatchIndex.value < highlights.length) {
      highlights[currentMatchIndex.value].classList.add('search-highlight-current')
    }
  }
}

function goToPreviousMatch() {
  if (searchMatches.value.length === 0) return
  
  currentMatchIndex.value = currentMatchIndex.value <= 0 
    ? searchMatches.value.length - 1 
    : currentMatchIndex.value - 1
  
  scrollToCurrentMatch()
}

function goToNextMatch() {
  if (searchMatches.value.length === 0) return
  
  currentMatchIndex.value = currentMatchIndex.value >= searchMatches.value.length - 1 
    ? 0 
    : currentMatchIndex.value + 1
  
  scrollToCurrentMatch()
}

// 防抖搜索文本
let searchDebounceTimer: ReturnType<typeof setTimeout> | null = null
watch(searchText, (newValue) => {
  if (searchDebounceTimer) {
    clearTimeout(searchDebounceTimer)
  }
  
  searchDebounceTimer = setTimeout(() => {
    debouncedSearchText.value = newValue
  }, 150) // 150ms 防抖延迟，提升响应速度
})

// 监听防抖搜索文本变化，清理缓存
watch(debouncedSearchText, () => {
  // 搜索文本变化时清理所有缓存
  filteredLogsCache.value.clear()
  
  // 重置所有滚动位置状态，避免搜索后滚动位置异常
  nextTick(() => {
    // 重置框架日志滚动位置
    userScrolled.value.set('framework', false)
    scrollPositionMemory.value.delete('framework')
    safeUpdateFrameworkVirtualList()
    
    // 重置操作日志滚动位置
    userScrolled.value.set('operation', false)
    scrollPositionMemory.value.delete('operation')
    safeUpdateOperationVirtualList()
    
    // 重置组件日志滚动位置
    selectedComponents.value.forEach((component) => {
      const componentName = `component-${selectedComponents.value.indexOf(component)}`
      userScrolled.value.set(componentName, false)
      scrollPositionMemory.value.delete(componentName)
      
      const currentKey = componentVirtualListKeys.value.get(component) || 0
      componentVirtualListKeys.value.set(component, currentKey + 1)
    })
    
    // 更新区域筛选状态
    updateAreaFilterStatus()
    
    // 更新操作日志Interface选项，确保与全局搜索条件一致
    if (operationLogEnabled.value) {
      updateOperationInterfaceOptions()
    }
  })
})

// 组件搜索防抖
const componentSearchTimers = new Map<string, ReturnType<typeof setTimeout>>()
watch(componentSearchTexts, (newMap) => {
  // 清理所有现有的定时器
  componentSearchTimers.forEach(timer => clearTimeout(timer))
  componentSearchTimers.clear()
  
  // 为每个组件设置防抖
  newMap.forEach((value, component) => {
    const timer = setTimeout(() => {
      debouncedComponentSearchTexts.value.set(component, value)
    }, 150) // 150ms 防抖延迟，提升响应速度
    componentSearchTimers.set(component, timer)
  })
}, { deep: true })

// 调试日志组件搜索防抖
let debugLogSearchDebounceTimer: ReturnType<typeof setTimeout> | null = null
watch(debugLogSearchText, (newValue) => {
  if (debugLogSearchDebounceTimer) {
    clearTimeout(debugLogSearchDebounceTimer)
  }
  
  debugLogSearchDebounceTimer = setTimeout(() => {
    debouncedDebugLogSearchText.value = newValue
  }, 150) // 150ms 防抖延迟，提升响应速度
})

// 监听组件防抖搜索文本变化，清理缓存
watch(debouncedComponentSearchTexts, () => {
  // 组件搜索文本变化时清理所有缓存
  filteredLogsCache.value.clear()
  
  // 重置组件日志滚动位置状态，避免搜索后滚动位置异常
  nextTick(() => {
    selectedComponents.value.forEach((component) => {
      const componentName = `component-${selectedComponents.value.indexOf(component)}`
      userScrolled.value.set(componentName, false)
      scrollPositionMemory.value.delete(componentName)
      
      const currentKey = componentVirtualListKeys.value.get(component) || 0
      componentVirtualListKeys.value.set(component, currentKey + 1)
    })
    
    // 更新区域筛选状态
    updateAreaFilterStatus()
  })
}, { deep: true })

async function loadComponentLogs(component: string) {
  if (!component || !currentWorkspace.value) {
    console.log('缺少必要条件，退出加载')
    return
  }

  // 优先检查 hasWorkspaceData，但也提供容错机制
  if (!hasWorkspaceData.value) {
    console.log('hasWorkspaceData 为 false，尝试直接获取数据作为容错')
  }

  const fileData = fileDataStore.getFileData(currentWorkspace.value.id)
  
  if (!fileData) {
    console.log('没有文件数据，退出加载')
    return
  }

  try {
    // 加载调试日志到缓存
    await loadRuntimeLogs(fileData, component)
  } catch (error) {
    console.error('加载日志失败:', error)
  }
}

// 缓存调试日志内容，避免重复解析
const runtimeLogCache = ref<{ [workspaceId: string]: string[] }>({})

// 提供全局函数来清除缓存，供转储管理页面调用
if (typeof window !== 'undefined') {
  (window as any).__clearLogCache = (dumpType: 'framework' | 'operation' | 'app') => {
    const workspaceId = currentWorkspace.value?.id
    if (!workspaceId) return
    
    if (dumpType === 'app') {
      // 清除调试日志缓存
      delete runtimeLogCache.value[workspaceId]
      
      // 同时清除组件日志，强制重新加载
      componentLogs.value.clear()
      
      // 重新加载当前选中的组件
      if (selectedComponents.value.length > 0) {
        setTimeout(() => {
          selectedComponents.value.forEach(async (component) => {
            await loadComponentLogs(component)
            
            // 只为新组件初始化日志区域高度，保持已有组件的高度不变
            nextTick(() => {
              setTimeout(() => {
                // 如果组件还没有设置高度，才设置初始高度
                if (!componentLogHeights.value.has(component)) {
                  const componentHeight = Math.floor(window.innerHeight / 3)
                  componentLogHeights.value.set(component, componentHeight)
                }
              }, 100)
            })
          })
        }, 100)
      }
    } else if (dumpType === 'operation') {
      // 清除操作日志相关缓存
      operationLogs.value = [] // 清空原始数据
      
      // 清除过滤缓存
      const cacheKeysToDelete: string[] = []
      filteredLogsCache.value.forEach((_, key) => {
        if (key.startsWith('operation_logs_')) {
          cacheKeysToDelete.push(key)
        }
      })
      cacheKeysToDelete.forEach(key => filteredLogsCache.value.delete(key))
      
      // 强制重新加载操作日志
      setTimeout(() => {
        loadOperationLogs()
        
        // 重新初始化操作日志区域高度，设置为当前屏幕的一半
        nextTick(() => {
          setTimeout(() => {
            if (operationLogEnabled.value) {
              operationLogHeight.value = Math.floor(window.innerHeight / 3)
            }
          }, 100)
        })
      }, 100)
    } else if (dumpType === 'framework') {
      // 清除框架日志相关缓存
      frameworkLogs.value = [] // 清空原始数据
      
      // 清除过滤缓存
      const cacheKeysToDelete: string[] = []
      filteredLogsCache.value.forEach((_, key) => {
        if (key.startsWith('framework_logs_')) {
          cacheKeysToDelete.push(key)
        }
      })
      cacheKeysToDelete.forEach(key => filteredLogsCache.value.delete(key))
      
      // 强制重新加载框架日志
      setTimeout(() => {
        loadFrameworkLogs()
        
        // 重新初始化框架日志区域高度，设置为当前屏幕的一半
        nextTick(() => {
          setTimeout(() => {
            if (frameworkLogEnabled.value) {
              frameworkLogHeight.value = Math.floor(window.innerHeight / 3)
            }
          }, 100)
        })
      }, 100)
    }
  }
  
  // 提供时间过滤信息调试函数
  (window as any).__getTimeFilterInfo = () => {
    return {
      startTime: startTime.value,
      endTime: endTime.value,
      hasTimeFilter: !!(startTime.value && endTime.value)
    }
  }
}

async function loadRuntimeLogs(fileData: string, component: string) {
  
  const workspaceId = currentWorkspace.value?.id
  if (!workspaceId) return
  
      // 检查调试日志缓存
  let lines: string[]
  if (runtimeLogCache.value[workspaceId]) {
    lines = runtimeLogCache.value[workspaceId]
    
  } else {
    // 1. 加载原始调试日志
    lines = []
    const runtimeContent = extractFileContent(fileData, LOG_PATHS.LOG_DUMP.APP)
    if (runtimeContent) {
      lines.push(...runtimeContent.split('\n'))
    } else {
      console.warn('未找到原始调试日志内容')
    }
    
    // 2. 加载转储调试日志
    try {
      const dumpLogs = await getAllDumpLogs(workspaceId, 'app')
      
      if (dumpLogs.length === 0) {
        console.log('没有找到任何转储日志文件，请检查转储管理页面的纳入分析开关')
      }
      
      for (const dump of dumpLogs) {
        const dumpLines = dump.data.split('\n')
        
        // 检查转储文件中的 account 日志样本
        const accountSample = dumpLines.filter(line => line.includes('account')).slice(0, 3)
        
        // 直接添加转储文件的行，不需要标记，因为它们就是原始的日志格式
        lines.push(...dumpLines)
      }
    } catch (error) {
      console.warn('加载调试转储日志失败:', error)
    }
    
    
    // 缓存所有调试日志行（原始 + 转储）
    runtimeLogCache.value[workspaceId] = lines
  }
  
  const componentLogsArray: any[] = []
  let matchedLines = 0
  
  // 使用更高效的字符串匹配
  const componentPattern = new RegExp(`\\b${component}\\b`)
  
  for (let i = 0; i < lines.length; i++) {
    const line = lines[i].trim()
    if (line && componentPattern.test(line)) {
      matchedLines++
      const parsedLog = parseRuntimeLogLine(line)
      if (parsedLog && parsedLog.component === component) {
        // 只应用时间过滤，级别和搜索过滤在显示时应用
        if (isLogInTimeRange(parsedLog.timestamp)) {
          componentLogsArray.push({
            timestamp: parsedLog.timestamp,
            component: parsedLog.component,
            level: parsedLog.level,
            message: parsedLog.message
          })
        }
      }
    }
  }
  
  componentLogs.value.set(component, componentLogsArray)
  
  // 只为新组件初始化日志区域高度，保持已有组件的高度不变
  nextTick(() => {
    setTimeout(() => {
      // 如果组件还没有设置高度，才设置初始高度
      if (!componentLogHeights.value.has(component)) {
        const componentHeight = Math.floor(window.innerHeight / 3)
        componentLogHeights.value.set(component, componentHeight)
      }
    }, 100) // 延迟100ms确保DOM完全渲染
  })
  
  // 清除该组件的过滤缓存，强制重新计算
  const cacheKeysToDelete: string[] = []
  filteredLogsCache.value.forEach((_, key) => {
    if (key.startsWith(`${component}_`)) {
      cacheKeysToDelete.push(key)
    }
  })
  cacheKeysToDelete.forEach(key => filteredLogsCache.value.delete(key))
  
  // 强制触发UI更新
  nextTick(() => {
    // 强制虚拟列表重新渲染
    const currentKey = componentVirtualListKeys.value.get(component) || 0
    componentVirtualListKeys.value.set(component, currentKey + 1)
  })
}

function getComponentLogs(component: string): any[] {
  // 使用实时过滤，而不是缓存的日志
  return getFilteredComponentLogs(component)
}

function getComponentLogStats(component: string) {
  // 获取该组件的所有日志（只应用时间过滤）
  const allLogs = getAllComponentLogs(component)
  
  // 应用搜索过滤到统计信息
  const filteredLogs = allLogs.filter(log => isLogMatchSearch(log, component))
  return calculateLogStats(filteredLogs)
}

// 获取组件的日志级别列表
function getComponentLogLevels(component: string): string[] {
  const stats = getComponentLogStats(component)
  return stats.map(stat => stat.level)
}

// 获取组件特定级别的日志数量
function getComponentLogLevelCount(component: string, level: string): number {
  const stats = getComponentLogStats(component)
  const levelStat = stats.find(stat => stat.level.toLowerCase() === level.toLowerCase())
  return levelStat ? levelStat.total : 0
}

function getAllComponentLogs(component: string): any[] {
  
  if (!component || !currentWorkspace.value) {
    console.log('getAllComponentLogs: 缺少必要条件')
    return []
  }

  const workspaceId = currentWorkspace.value.id
  
  // 使用缓存的调试日志，如果没有缓存则尝试重新加载
  let lines = runtimeLogCache.value[workspaceId]
  if (!lines) {
    
    // 尝试直接从文件数据中提取
    const fileData = fileDataStore.getFileData(workspaceId)
    if (fileData) {
      const runtimeContent = extractFileContent(fileData, LOG_PATHS.LOG_DUMP.APP)
      if (runtimeContent) {
        lines = runtimeContent.split('\n')
        runtimeLogCache.value[workspaceId] = lines
      } else {
        console.log('getAllComponentLogs: 无法提取运行时内容')
        return []
      }
    } else {
      console.log('getAllComponentLogs: 无法获取文件数据')
      return []
    }
  }

  const allLogs: any[] = []
  
  // 使用更高效的字符串匹配
  const componentPattern = new RegExp(`\\b${component}\\b`)
  
  for (let i = 0; i < lines.length; i++) {
    const line = lines[i].trim()
    if (line && componentPattern.test(line)) {
      const parsedLog = parseRuntimeLogLine(line)
      if (parsedLog && parsedLog.component === component) {
        // 只应用时间过滤，不应用级别过滤
        if (isLogInTimeRange(parsedLog.timestamp)) {
          allLogs.push({
            timestamp: parsedLog.timestamp,
            component: parsedLog.component,
            level: parsedLog.level,
            message: parsedLog.message
          })
        }
      }
    }
  }

  return allLogs
}

// 检查组件是否有日志信息
function hasComponentLogs(component: string): boolean {
  const logs = getAllComponentLogs(component)
  return logs.length > 0
}

function getFilteredComponentLogs(component: string): any[] {
  const currentCacheKey = cacheKey.value
  const cacheKeyForComponent = `${component}_${currentCacheKey}`
  
  // 检查缓存
  if (filteredLogsCache.value.has(cacheKeyForComponent)) {
    return filteredLogsCache.value.get(cacheKeyForComponent)!
  }
  
  // 获取所有日志（只应用时间过滤）
  const allLogs = getAllComponentLogs(component)
  
     // 应用级别过滤和搜索过滤
   const filteredLogs = allLogs.filter(log => {
     // 级别过滤
     if (!isLevelEnabled(component, log.level)) {
       return false
     }
     
     // 搜索过滤
     if (!isLogMatchSearch(log, component)) {
       return false
     }
     
     return true
   })
  
  // 缓存结果
  filteredLogsCache.value.set(cacheKeyForComponent, filteredLogs)
  
  return filteredLogs
}

function removeComponent(component: string) {
  const index = selectedComponents.value.indexOf(component)
  if (index > -1) {
    // 保存当前选中的组件列表
    const currentComponents = [...selectedComponents.value]
    
    // 移除组件
    selectedComponents.value.splice(index, 1)
    componentLogs.value.delete(component)
    levelFilters.value.delete(component) // 清理级别过滤数据
    
    // 清理组件搜索数据
    componentSearchTexts.value.delete(component)
    debouncedComponentSearchTexts.value.delete(component)
    
    // 清理该组件的缓存
    const cacheKeysToDelete: string[] = []
    filteredLogsCache.value.forEach((_, key) => {
      if (key.startsWith(`${component}_`)) {
        cacheKeysToDelete.push(key)
      }
    })
    cacheKeysToDelete.forEach(key => filteredLogsCache.value.delete(key))
    
    // 清理滚动位置状态
    const componentName = `component-${index}`
    userScrolled.value.delete(componentName)
    scrollPositionMemory.value.delete(componentName)
    
    // 强制清除调试日志缓存，确保重新加载转储数据
    const workspaceId = currentWorkspace.value?.id
    if (workspaceId && runtimeLogCache.value[workspaceId]) {
      delete runtimeLogCache.value[workspaceId]
    }
    
    // 强制重新加载剩余组件的日志，确保数据刷新
    if (selectedComponents.value.length > 0) {
      setTimeout(() => {
        selectedComponents.value.forEach(async (remainingComponent) => {
          await loadComponentLogs(remainingComponent)
        })
      }, 100)
    }
    
  }
}

function isLogInTimeRange(logTimestamp: string): boolean {
  if (!startTime.value && !endTime.value) {
    return true // 没有设置时间范围，显示所有日志
  }
  
  try {
    const logTime = new Date(logTimestamp).getTime()
    
    if (startTime.value && logTime < startTime.value) {
      return false
    }
    
    if (endTime.value && logTime > endTime.value) {
      return false
    }
    
    return true
  } catch (error) {
    console.warn('解析日志时间失败:', logTimestamp, error)
    return true // 解析失败时显示该日志
  }
}

function isLogMatchSearch(log: any, component: string): boolean {
  // 检查全局搜索
  const globalSearchText = debouncedSearchText.value
  const globalMatch = !globalSearchText || globalSearchText.trim() === '' || 
    `${log.timestamp} ${log.component} ${log.level} ${log.message}`.toLowerCase().includes(globalSearchText.toLowerCase().trim())
  
  if (!globalMatch) {
    return false // 全局搜索不匹配，直接返回false
  }
  
  // 检查排除关键词
  const logContent = `${log.timestamp} ${log.component} ${log.level} ${log.message}`.toLowerCase()
  for (const excludeKeyword of excludeKeywords.value) {
    if (excludeKeyword.trim() && logContent.includes(excludeKeyword.toLowerCase().trim())) {
      return false // 包含排除关键词，直接返回false
    }
  }
  
  // 检查组件搜索
  const componentSearchText = debouncedComponentSearchTexts.value.get(component)
  const componentMatch = !componentSearchText || componentSearchText.trim() === '' || 
    `${log.timestamp} ${log.component} ${log.level} ${log.message}`.toLowerCase().includes(componentSearchText.toLowerCase().trim())
  
  return componentMatch
}

function parseRuntimeLogLine(line: string) {
  // 解析调试日志格式: 2025-07-18 11:05:21.750324 pcie_device ERROR: pcie_card.lua(49): PCIe card oob management init failed.
  const match = line.match(/^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}\.\d+)\s+([a-zA-Z_][a-zA-Z0-9_]*)\s+([A-Z]+):\s*(.+)$/)
  if (match) {
    return {
      timestamp: match[1],
      component: match[2],
      level: match[3],
      message: match[4]
    }
  }
  return null
}

function calculateLogStats(logs: any[]) {
  const stats = new Map<string, number>()
  
  logs.forEach(log => {
    const level = log.level?.toLowerCase() || 'unknown'
    stats.set(level, (stats.get(level) || 0) + 1)
  })
  
  return Array.from(stats.entries()).map(([level, total]) => ({
    level,
    total
  }))
}

function getLogLevelClass(level: string) {
  const levelLower = level.toLowerCase()
  switch (levelLower) {
    case 'error':
      return 'log-level log-level-error'
    case 'warn':
    case 'warning':
      return 'log-level log-level-warn'
    case 'info':
      return 'log-level log-level-info'
    case 'debug':
      return 'log-level log-level-debug'
    case 'skynet':
      return 'log-level log-level-skynet'
    default:
      return 'log-level log-level-unknown'
  }
}

function getInterfaceClass(interfaceName: string) {
  const interfaceLower = interfaceName.toLowerCase()
  switch (interfaceLower) {
    case 'redfish':
      return 'log-interface log-interface-redfish'
    case 'web':
      return 'log-interface log-interface-web'
    case 'ipmi':
      return 'log-interface log-interface-ipmi'
    case 'cli':
      return 'log-interface log-interface-cli'
    case 'busctl':
      return 'log-interface log-interface-busctl'
    case 'mdbctl':
      return 'log-interface log-interface-mdbctl'
    default:
      return 'log-interface log-interface-unknown'
  }
}

function clearLogs() {
  componentLogs.value.clear()
}

// 级别过滤相关函数
function isLevelEnabled(component: string, level: string): boolean {
  const enabledLevels = levelFilters.value.get(component)
  const levelLower = level.toLowerCase()
  
  // 如果没有设置过滤，则默认启用所有级别
  if (!enabledLevels) {
    return true
  }
  
  // 如果过滤集合为空，说明用户手动关闭了所有级别，此时应该返回false
  if (enabledLevels.size === 0) {
    return false
  }
  
  const isEnabled = enabledLevels.has(levelLower)
  return isEnabled
}

function toggleLevelFilter(component: string, level: string, enabled: boolean) {
  
  const levelLower = level.toLowerCase()
  
  if (!levelFilters.value.has(component)) {
    // 如果组件还没有过滤设置，且要启用级别，则不需要创建过滤（保持默认启用所有）
    if (enabled) {
      return
    }
    // 如果要禁用级别，则创建过滤集合并添加除了当前级别之外的所有级别
    const allLevels = getComponentLogStats(component).map(stat => stat.level.toLowerCase())
    const enabledLevels = new Set(allLevels.filter(level => level !== levelLower))
    levelFilters.value.set(component, enabledLevels)
  } else {
    const enabledLevels = levelFilters.value.get(component)!
    
    if (enabled) {
      enabledLevels.add(levelLower)
      
      // 如果所有级别都启用了，可以删除过滤设置（回到默认状态）
      const allLevels = getComponentLogStats(component).map(stat => stat.level.toLowerCase())
      const allEnabled = allLevels.every(level => enabledLevels.has(level))
      if (allEnabled) {
        levelFilters.value.delete(component)
      }
    } else {
      // 直接删除当前级别
      enabledLevels.delete(levelLower)
    }
  }
  
  
  // 强制触发响应式更新
  levelFilters.value = new Map(levelFilters.value)
  
  // 清理该组件的缓存，因为级别过滤发生了变化
  const cacheKeysToDelete: string[] = []
  filteredLogsCache.value.forEach((_, key) => {
    if (key.startsWith(`${component}_`)) {
      cacheKeysToDelete.push(key)
    }
  })
  cacheKeysToDelete.forEach(key => filteredLogsCache.value.delete(key))
  
  // 重置滚动位置状态，避免过滤后滚动位置异常
  nextTick(() => {
    // 重置用户滚动状态，允许自动滚动到底部
    userScrolled.value.set(component, false)
    // 清理保存的滚动位置
    scrollPositionMemory.value.delete(component)
    
    // 强制虚拟列表重新渲染，彻底解决滚动条消失问题
    const currentKey = componentVirtualListKeys.value.get(component) || 0
    componentVirtualListKeys.value.set(component, currentKey + 1)
  })
}

// 操作日志相关函数
const operationLogs = ref<any[]>([])
const operationSearchText = ref<string>('')
const debouncedOperationSearchText = ref<string>('')

// 告警日志相关变量
const alertLogs = ref<any[]>([])
const alertLogColumns = ref<any[]>([])
const alertLogSearchText = ref<string>('')

// 告警日志筛选现在通过列头筛选器实现

// 操作日志Interface多选相关变量
const selectedOperationInterfaces = ref<string[]>([])
const operationInterfaceOptions = ref<Array<{ label: string, value: string }>>([])

// 操作日志interface筛选相关函数
function isInterfaceEnabled(interfaceName: string): boolean {
  const interfaceLower = interfaceName.toLowerCase()
  
  // 如果没有设置过滤，则默认启用所有interface
  if (interfaceFilters.value.length === 0) {
    return true
  }
  
  // 如果interfaceFilters包含某个interface名称，表示该interface被禁用了
  const isEnabled = !interfaceFilters.value.includes(interfaceLower)
  return isEnabled
}

function toggleInterfaceFilter(interfaceName: string, enabled: boolean) {
  
  const interfaceLower = interfaceName.toLowerCase()
  
  if (enabled) {
    // 如果要启用interface，则从禁用集合中删除
    interfaceFilters.value = interfaceFilters.value.filter(name => name !== interfaceLower)
  } else {
    // 如果要禁用interface，则添加到禁用集合中
    if (!interfaceFilters.value.includes(interfaceLower)) {
      interfaceFilters.value.push(interfaceLower)
    }
  }
  
  
  // 强制触发响应式更新
  interfaceFilters.value = [...interfaceFilters.value]
  
  // 清理操作日志缓存，因为interface过滤发生了变化
  const cacheKeysToDelete: string[] = []
  filteredLogsCache.value.forEach((_, key) => {
    if (key.startsWith('operation_logs_')) {
      cacheKeysToDelete.push(key)
    }
  })
  cacheKeysToDelete.forEach(key => filteredLogsCache.value.delete(key))
  
  // 重置滚动位置状态，避免过滤后滚动位置异常
  nextTick(() => {
    // 重置用户滚动状态，允许自动滚动到底部
    userScrolled.value.set('operation', false)
    // 清理保存的滚动位置
    scrollPositionMemory.value.delete('operation')
    
    // 强制虚拟列表重新渲染
    safeUpdateOperationVirtualList()
    
  })
}

function getOperationLogs(): any[] {
  if (!operationLogEnabled.value) {
    return []
  }
  
  const filteredLogs = getFilteredOperationLogs()
  return filteredLogs
}

// 仅用于统计：忽略 interface 过滤，只应用时间与搜索过滤，避免统计项消失
function getOperationLogsForStats(): any[] {
  if (!operationLogEnabled.value) {
    return []
  }
  const allLogs = operationLogs.value
  return allLogs.filter(log => {
    if (!isOperationLogMatchSearch(log)) return false
    if (!isLogInTimeRange(log.timestamp)) return false
    return true
  })
}

function getOperationLogStats() { 
  // 使用过滤后的日志来计算统计信息，确保与日志列表显示一致
  // 改为仅受时间与搜索影响，不受 interface 过滤影响，避免行消失
  const logsForStats = getOperationLogsForStats()
  const stats = calculateOperationLogStats(logsForStats)
  return stats
}

// 操作日志Interface多选相关函数
function onOperationInterfaceChange(selectedInterfaces: string[]) {
  selectedOperationInterfaces.value = selectedInterfaces
  
  // 更新interface过滤逻辑
  // 如果某个interface不在选中列表中，则禁用它
  const allInterfaces = getOperationLogInterfaces()
  const disabledInterfaces = allInterfaces.filter((iface: string) => !selectedInterfaces.includes(iface))
  
  // 更新interfaceFilters
  interfaceFilters.value = disabledInterfaces.map((iface: string) => iface.toLowerCase())
  
  
  // 清理操作日志缓存，因为interface过滤发生了变化
  const cacheKeysToDelete: string[] = []
  filteredLogsCache.value.forEach((_, key) => {
    if (key.startsWith('operation_logs_')) {
      cacheKeysToDelete.push(key)
    }
  })
  cacheKeysToDelete.forEach(key => filteredLogsCache.value.delete(key))
  
  // 重置滚动位置状态，避免过滤后滚动位置异常
  nextTick(() => {
    // 重置用户滚动状态，允许自动滚动到底部
    userScrolled.value.set('operation', false)
    // 清理保存的滚动位置
    scrollPositionMemory.value.delete('operation')
    
    // 强制虚拟列表重新渲染
    safeUpdateOperationVirtualList()
    
  })
}

// 获取操作日志的Interface列表
function getOperationLogInterfaces(): string[] {
  const stats = getOperationLogStats()
  return stats.map(stat => stat.interface)
}

// 获取操作日志的Interface列表（基于当前筛选条件）
function getFilteredOperationLogInterfaces(): string[] {
  // 使用筛选后的日志来计算统计信息，确保与当前显示一致
  const filteredLogs = getFilteredOperationLogs()
  const stats = calculateOperationLogStats(filteredLogs)
  return stats.map(stat => stat.interface)
}

// 获取框架日志的级别列表
function getFrameworkLogLevels(): string[] {
  const stats = getFrameworkLogStats()
  return stats.map(stat => stat.level)
}

// 获取框架日志特定级别的数量
function getFrameworkLogLevelCount(level: string): number {
  const stats = getFrameworkLogStats()
  const levelStat = stats.find(stat => stat.level === level)
  return levelStat ? levelStat.total : 0
}

// 检查框架日志级别是否启用
function isFrameworkLevelEnabled(level: string): boolean {
  const isEnabled = frameworkLevelFilters.value.get(level)
  // 如果没有设置过滤，则默认启用所有级别
  return isEnabled === undefined || isEnabled
}

// 获取操作日志特定Interface的数量
function getOperationInterfaceCount(iface: string): number {
  const stats = getOperationLogStats()
  const ifaceStat = stats.find(stat => stat.interface === iface)
  return ifaceStat ? ifaceStat.total : 0
}

// 获取操作日志特定Interface的数量（基于当前筛选条件）
function getFilteredOperationInterfaceCount(iface: string): number {
  const filteredLogs = getFilteredOperationLogs()
  const stats = calculateOperationLogStats(filteredLogs)
  const ifaceStat = stats.find(stat => stat.interface === iface)
  return ifaceStat ? ifaceStat.total : 0
}

// 初始化操作日志Interface选项
function initOperationInterfaceOptions() {
  const interfaces = getOperationLogInterfaces()
  operationInterfaceOptions.value = interfaces.map((iface: string) => ({
    label: `${iface}(${getOperationInterfaceCount(iface)})`,
    value: iface
  }))
  
  // 默认全选中
  selectedOperationInterfaces.value = [...interfaces]
  
}

// 更新操作日志Interface选项（基于当前筛选条件）
function updateOperationInterfaceOptions() {
  // 检查是否有筛选条件
  const hasFilters = debouncedOperationSearchText.value || (startTime.value && endTime.value) || debouncedSearchText.value || excludeKeywords.value.length > 0
  
  if (hasFilters) {
    // 有筛选条件时，使用筛选后的Interface列表
    const filteredInterfaces = getFilteredOperationLogInterfaces()
    
    // 更新选项列表，使用筛选后的数量
    operationInterfaceOptions.value = filteredInterfaces.map((iface: string) => ({
      label: `${iface}(${getFilteredOperationInterfaceCount(iface)})`,
      value: iface
    }))
    
    // 更新选中的Interface，只保留仍然可用的
    selectedOperationInterfaces.value = selectedOperationInterfaces.value.filter(iface => 
      filteredInterfaces.includes(iface)
    )
    
  } else {
    // 没有筛选条件时，恢复显示所有Interface选项
    const allInterfaces = getOperationLogInterfaces()
    
    // 恢复所有Interface选项
    operationInterfaceOptions.value = allInterfaces.map((iface: string) => ({
      label: `${iface}(${getOperationInterfaceCount(iface)})`,
      value: iface
    }))
    
    // 恢复所有Interface为选中状态
    selectedOperationInterfaces.value = [...allInterfaces]
  }
}

// 自定义渲染Interface标签
function renderInterfaceTag({ option, handleClose }: { option: any, handleClose: () => void }) {
  const index = selectedOperationInterfaces.value.indexOf(option.value)
  const isFirst = index === 0
  
  if (isFirst) {
    // 第一个标签显示完整信息，并添加其他选中数量的提示
    const extraCount = selectedOperationInterfaces.value.length - 1
    const extraCountText = extraCount > 0 ? `+${extraCount}` : ''
    
    return h('div', {
      class: 'custom-interface-tag',
      'data-extra-count': extraCountText
    }, [
      h('span', { class: 'tag-text' }, option.label),
      extraCount > 0 ? h('span', { class: 'extra-count' }, extraCountText) : null,
      h('button', {
        class: 'tag-close-btn',
        onClick: (e: Event) => {
          e.stopPropagation()
          handleClose()
        }
      }, '×')
    ])
  } else {
    // 其他标签不显示
    return null
  }
}

function calculateOperationLogStats(logs: any[]) {
  const stats = new Map<string, number>()
  
  logs.forEach(log => {
    const interfaceName = log.interface || 'unknown'
    stats.set(interfaceName, (stats.get(interfaceName) || 0) + 1)
  })
  
  return Array.from(stats.entries()).map(([interfaceName, total]) => ({
    interface: interfaceName,
    total
  }))
}

function getOperationStatsColumns() {
  return [
    {
      title: 'Interface',
      key: 'interface',
      width: 120
    },
    {
      title: 'Total',
      key: 'total',
      width: 80
    },
    {
      title: 'Filter',
      key: 'filter',
      width: 80,
      render: (row: any) => {
        return h(NSwitch, {
          value: isInterfaceEnabled(row.interface),
          size: 'small',
          onUpdateValue: (value: boolean) => {
            toggleInterfaceFilter(row.interface, value)
          }
        })
      }
    }
  ]
}

async function loadOperationLogs() {
  
  if (!currentWorkspace.value) {
    console.warn('没有工作区，无法加载操作日志')
    return
  }

  // 优先检查 hasWorkspaceData，但也提供容错机制
  if (!hasWorkspaceData.value) {
    console.log('loadOperationLogs: hasWorkspaceData 为 false，尝试直接获取数据')
  }

  const fileData = fileDataStore.getFileData(currentWorkspace.value.id)
  
  if (!fileData) {
    console.warn('没有文件数据，无法加载操作日志')
    return
  }

  const operationLogPath = LOG_PATHS.LOG_DUMP.OPERATION

  try {
    // 1. 加载原始操作日志
    const parsedLogs: any[] = []
    const operationContent = extractFileContent(fileData, operationLogPath)
    if (operationContent) {
      const lines = operationContent.split('\n')
      for (const line of lines) {
        const trimmedLine = line.trim()
        if (trimmedLine) {
          const parsedLog = parseOperationLogLine(trimmedLine)
          if (parsedLog) {
            parsedLogs.push(parsedLog)
          }
        }
      }
    } else {
      console.warn('未找到原始操作日志文件')
    }
    
    // 2. 加载转储操作日志
    try {
      const dumpLogs = await getAllDumpLogs(currentWorkspace.value.id, 'operation')
      
      for (const dump of dumpLogs) {
        const lines = dump.data.split('\n')
        
        for (const line of lines) {
          const trimmedLine = line.trim()
          if (trimmedLine) {
            const parsedLog = parseOperationLogLine(trimmedLine)
            if (parsedLog) {
              // 标记这是来自转储文件的日志
              const logWithSource = { ...parsedLog, source: `dump-${dump.index}` }
              parsedLogs.push(logWithSource)
            }
          }
        }
      }
    } catch (error) {
      console.warn('加载操作转储日志失败:', error)
    }
    
    // 3. 按时间戳排序所有日志（原始 + 转储）
    parsedLogs.sort((a, b) => {
      const timeA = new Date(a.timestamp).getTime()
      const timeB = new Date(b.timestamp).getTime()
      return timeA - timeB
    })
    
    operationLogs.value = parsedLogs
    
    // 立即初始化Interface选项，确保下拉框有数据
    nextTick(() => {
      initOperationInterfaceOptions()
    })
    
    // 4. 强制清理缓存，确保新数据能正确显示
    const cacheKeysToDelete: string[] = []
    filteredLogsCache.value.forEach((_, key) => {
      if (key.startsWith('operation_logs_')) {
        cacheKeysToDelete.push(key)
      }
    })
    cacheKeysToDelete.forEach(key => filteredLogsCache.value.delete(key))
    
    // 5. 强制UI更新
    nextTick(() => {
      safeUpdateOperationVirtualList()
      
      // 强制触发统计数据的响应式更新
      operationLogs.value = [...operationLogs.value]
    })
    
  } catch (error) {
    console.error('读取操作日志失败:', error)
    operationLogs.value = []
  }
}

function parseOperationLogLine(line: string) {
  
  // 解析操作日志格式: 2025-06-30 16:54:48 Redfish,Administrator@21.21.0.61,redfish,Delete event subscription successfully
  // 格式：时间 接口,用户,组件,消息
  // 先按逗号分割，然后处理第一部分（包含时间）
  const commaParts = line.split(',')
  
  if (commaParts.length >= 4) {
    // 第一部分包含时间和接口，需要分离
    const firstPart = commaParts[0] // "2025-06-30 16:54:48 Redfish"
    const timeMatch = firstPart.match(/^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})\s+(.+)$/)
    
    if (timeMatch) {
      const result = {
        timestamp: timeMatch[1], // "2025-06-30 16:54:48"
        interface: timeMatch[2], // "Redfish"
        user: commaParts[1], // "Administrator@21.21.0.61"
        component: commaParts[2], // "redfish"
        message: commaParts[3] // "Delete event subscription successfully"
      }
      return result
    }
  }
  
  // 如果解析失败，尝试其他格式
  const fallbackMatch = line.match(/^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})\s+(.+)$/)
  if (fallbackMatch) {
    const result = {
      timestamp: fallbackMatch[1],
      interface: 'Unknown',
      user: 'Unknown',
      component: 'Unknown',
      message: fallbackMatch[2]
    }
    return result
  }
  
  return null
}

// 加载告警日志函数
async function loadAlertLogs() {
  console.log('=== loadAlertLogs 被调用 ===')
  
  if (!currentWorkspace.value) {
    console.warn('没有工作区，无法加载告警日志')
    return
  }

  const fileData = fileDataStore.getFileData(currentWorkspace.value.id)
  
  if (!fileData) {
    console.warn('没有文件数据，无法加载告警日志')
    return
  }

  // 使用配置文件中定义的告警日志路径
  const alertLogPath = LOG_PATHS.APP_DUMP.EVENT
  console.log('尝试读取告警日志文件:', alertLogPath)

  try {
    // 从文件数据中提取CSV内容
    const csvContent = extractFileContent(fileData, alertLogPath)
    
    if (csvContent) {
      console.log(`成功读取告警日志CSV文件，内容长度: ${csvContent.length} 字符`)
      
      // 解析CSV内容
      const { columns, data } = parseCSVContent(csvContent)
      
      // 设置表格列和数据
      alertLogColumns.value = columns
      alertLogs.value = data
      
      // 更新筛选选项
      updateAlertLogFilterOptions(data)
      
      console.log(`告警日志解析完成，列数: ${columns.length}，数据行数: ${data.length}`)
      console.log('表头:', columns.map(col => col.title).join(', '))
      
    } else {
      console.warn(`未找到告警日志文件: ${alertLogPath}`)
      alertLogColumns.value = []
      alertLogs.value = []
    }
    
  } catch (error) {
    console.error('读取告警日志失败:', error)
    alertLogColumns.value = []
    alertLogs.value = []
  }
 }
 
 // 解析CSV内容函数
 function parseCSVContent(csvContent: string): { columns: any[], data: any[] } {
   console.log('开始解析CSV内容...')
   
   const lines = csvContent.split('\n').filter(line => line.trim())
   
   if (lines.length === 0) {
     console.warn('CSV内容为空')
     return { columns: [], data: [] }
   }
   
   // 解析表头
   const headerLine = lines[0]
   const headers = parseCSVLine(headerLine)
   
   console.log('CSV表头:', headers)
   
   // 定义列顺序映射
  const columnOrder = ['Event Code', 'Event Type', 'Severity', 'Status', 'Event Description', 'Suggestion', 'Generation Time']
  
  // 创建表格列配置，过滤掉ID列（第一列）并按指定顺序排列
  const filteredHeaders = headers.slice(1)
  const columns: any[] = []
  
  // 按照指定顺序创建列
  columnOrder.forEach((orderTitle, orderIndex) => {
    const headerIndex = filteredHeaders.findIndex(header => header === orderTitle)
    if (headerIndex !== -1) {
      const columnConfig: any = {
        title: orderTitle,
        key: `col_${orderIndex + 1}`,
        width: orderTitle === 'Generation Time' ? 200 : 150,
        ellipsis: {
          tooltip: true
        },
        originalIndex: headerIndex
      }
      
      // 为可筛选的列添加筛选功能
      if (['Event Code', 'Event Type', 'Severity', 'Status'].includes(orderTitle)) {
        columnConfig.filter = true
        columnConfig.filterOptions = []
        columnConfig.filterOptionValue = null
        columnConfig.filterMultiple = true
        columnConfig.filterOptionValues = []
        columnConfig.onFilter = (value: any, row: any) => {
          if (!value || value.length === 0) return true
          const cellValue = row[columnConfig.key]
          return Array.isArray(value) ? value.includes(cellValue) : cellValue === value
        }
      }
      
      columns.push(columnConfig)
    }
  })
  
  // 添加任何未在指定顺序中的列
  filteredHeaders.forEach((header, index) => {
    if (!columnOrder.includes(header)) {
      columns.push({
        title: header,
        key: `col_${columns.length + 1}`,
        width: 150,
        ellipsis: {
          tooltip: true
        },
        originalIndex: index
      })
    }
  })
   
   // 解析数据行
   const data: any[] = []
   
   for (let i = 1; i < lines.length; i++) {
     const line = lines[i].trim()
     if (line) {
       const values = parseCSVLine(line)
       
       // 创建数据对象，跳过ID列（第一列）并按新的列顺序映射
       const rowData: any = {}
       columns.forEach((column, columnIndex) => {
         const originalIndex = column.originalIndex
         rowData[column.key] = values[originalIndex + 1] || ''
       })
       
       data.push(rowData)
     }
   }
   
   console.log(`CSV解析完成，表头数: ${headers.length}，数据行数: ${data.length}`)
   
   return { columns, data }
 }
 
 // 解析CSV行（处理逗号分隔和引号包围的字段）
 function parseCSVLine(line: string): string[] {
   const result: string[] = []
   let current = ''
   let inQuotes = false
   
   for (let i = 0; i < line.length; i++) {
     const char = line[i]
     
     if (char === '"') {
       if (inQuotes && line[i + 1] === '"') {
         // 双引号转义
         current += '"'
         i++ // 跳过下一个引号
       } else {
         // 切换引号状态
         inQuotes = !inQuotes
       }
     } else if (char === ',' && !inQuotes) {
       // 字段分隔符
       result.push(current.trim())
       current = ''
     } else {
       current += char
     }
   }
   
   // 添加最后一个字段
   result.push(current.trim())
   
   return result
 }
 
 // 处理告警日志筛选更新
function handleAlertLogFiltersUpdate(filters: Record<string, any>) {
  console.log('告警日志筛选更新:', filters)
  
  alertLogColumns.value.forEach(column => {
    if (filters[column.key] !== undefined) {
      column.filterOptionValues = filters[column.key]
      console.log(`列 ${column.title} (${column.key}) 筛选值更新为:`, filters[column.key])
    }
  })
  
  console.log('当前所有列的筛选状态:', alertLogColumns.value.map(col => ({
    title: col.title,
    key: col.key,
    filterOptionValues: col.filterOptionValues
  })))
}
 
 // 更新告警日志筛选选项
 function updateAlertLogFilterOptions(data: any[]) {
   // 提取各列的唯一值
   const eventCodes = [...new Set(data.map(item => item.col_1).filter(Boolean))]
   const eventTypes = [...new Set(data.map(item => item.col_2).filter(Boolean))]
   const severities = [...new Set(data.map(item => item.col_3).filter(Boolean))]
   const statuses = [...new Set(data.map(item => item.col_4).filter(Boolean))]
   
   // 更新列配置中的筛选选项
   alertLogColumns.value.forEach(column => {
     if (column.title === 'Event Code') {
       column.filterOptions = eventCodes.map(code => ({ label: code, value: code }))
       column.filterMultiple = true
     } else if (column.title === 'Event Type') {
       column.filterOptions = eventTypes.map(type => ({ label: type, value: type }))
       column.filterMultiple = true
     } else if (column.title === 'Severity') {
       column.filterOptions = severities.map(severity => ({ label: severity, value: severity }))
       column.filterMultiple = true
     } else if (column.title === 'Status') {
       column.filterOptions = statuses.map(status => ({ label: status, value: status }))
       column.filterMultiple = true
     }
   })
   
   console.log('列头筛选选项已更新:', {
     eventCodes: eventCodes.length,
     eventTypes: eventTypes.length,
     severities: severities.length,
     statuses: statuses.length
   })
 }
 
 function getFilteredOperationLogs(): any[] {
  
  const excludeKeywordsStr = excludeKeywords.value.join('|')
  const operationCacheKey = `${debouncedOperationSearchText.value}_${startTime.value}_${endTime.value}_${JSON.stringify(interfaceFilters.value)}_${excludeKeywordsStr}`
  const cacheKeyForOperationLogs = `operation_logs_${operationCacheKey}`
  

  if (filteredLogsCache.value.has(cacheKeyForOperationLogs)) {
    const cachedLogs = filteredLogsCache.value.get(cacheKeyForOperationLogs)!
    return cachedLogs
  }

  const allLogs = operationLogs.value
  
  if (allLogs.length === 0) {
    filteredLogsCache.value.set(cacheKeyForOperationLogs, [])
    return []
  }
  
  const filteredLogs = allLogs.filter(log => {
    // 搜索过滤
    if (!isOperationLogMatchSearch(log)) {
      return false
    }

    // 时间过滤
    if (!isLogInTimeRange(log.timestamp)) {
      return false
    }

    // interface过滤
    if (!isInterfaceEnabled(log.interface)) {
      return false
    }

    return true
  })

  filteredLogsCache.value.set(cacheKeyForOperationLogs, filteredLogs)
  return filteredLogs
}

function isOperationLogMatchSearch(log: any): boolean {
  
  // 检查全局搜索
  const globalSearchText = debouncedSearchText.value
  if (globalSearchText && globalSearchText.trim() !== '') {
    const globalSearchLower = globalSearchText.toLowerCase().trim()
    const logText = `${log.timestamp} ${log.interface} ${log.user},${log.component},${log.message}`.toLowerCase()
    if (!logText.includes(globalSearchLower)) {
      console.log(`全局搜索不匹配: "${globalSearchLower}" vs "${logText.substring(0, 50)}..."`)
      return false // 全局搜索不匹配，直接返回false
    }
  }
  
  // 检查排除关键词
  const logContent = `${log.timestamp} ${log.interface} ${log.user},${log.component},${log.message}`.toLowerCase()
  for (const excludeKeyword of excludeKeywords.value) {
    if (excludeKeyword.trim() && logContent.includes(excludeKeyword.toLowerCase().trim())) {
      return false // 包含排除关键词，直接返回false
    }
  }
  
  // 检查操作日志专用搜索
  const searchText = debouncedOperationSearchText.value
  if (!searchText || searchText.trim() === '') {
    return true
  }

  const searchLower = searchText.toLowerCase().trim()
  const logText = `${log.timestamp} ${log.interface} ${log.user},${log.component},${log.message}`.toLowerCase()
  
  const isMatch = logText.includes(searchLower)
  
  return isMatch
}

function onOperationSearchChange(value: string) {
  operationSearchText.value = value
  
  // 清理操作日志缓存，确保搜索过滤生效
  const cacheKeysToDelete: string[] = []
  filteredLogsCache.value.forEach((_, key) => {
    if (key.startsWith('operation_logs_')) {
      cacheKeysToDelete.push(key)
    }
  })
  cacheKeysToDelete.forEach(key => filteredLogsCache.value.delete(key))
  
  // 强制UI更新，确保统计表格和日志列表同步
  nextTick(() => {
    safeUpdateOperationVirtualList()
  })
}

function removeOperationLog() {
  operationLogEnabled.value = false
  operationLogs.value = []
  operationSearchText.value = ''
  debouncedOperationSearchText.value = ''
  interfaceFilters.value = [] // 清理interface筛选数据
  
  // 清理缓存
  const cacheKeysToDelete: string[] = []
  filteredLogsCache.value.forEach((_, key) => {
    if (key.startsWith('operation_logs_')) {
      cacheKeysToDelete.push(key)
    }
  })
  cacheKeysToDelete.forEach(key => filteredLogsCache.value.delete(key))
  
  // 清理滚动位置状态
  userScrolled.value.delete('operation')
  scrollPositionMemory.value.delete('operation')
  
  // 重置其他日志类型的虚拟列表，因为DOM结构变化
  nextTick(() => {
    if (frameworkLogEnabled.value) {
      safeUpdateFrameworkVirtualList()
    }
  })
  
  // 重置操作日志区域高度
  operationLogHeight.value = Math.floor(window.innerHeight / 3)
}

// 监听操作日志开关变化
watch(operationLogEnabled, (newValue) => {
  
  if (newValue) {
    // 当操作日志开关打开时，加载操作日志
    loadOperationLogs()
    
    // 重置滚动位置状态
    nextTick(() => {
      userScrolled.value.set('operation', false)
      scrollPositionMemory.value.delete('operation')
      
      // 强制虚拟列表重新渲染，彻底解决滚动条消失问题
      safeUpdateOperationVirtualList()
      
      // 自动聚焦到新增的操作日志面板
      scrollToLogPanel('operation')
      
      // 当操作日志显示时，也重置框架日志，因为DOM结构变化可能影响框架日志的滚动
      if (frameworkLogEnabled.value) {
        userScrolled.value.set('framework', false)
        scrollPositionMemory.value.delete('framework')
        safeUpdateFrameworkVirtualList()
      }
      
      // 初始化操作日志区域高度，设置为当前屏幕的1/3
      setTimeout(() => {
        operationLogHeight.value = Math.floor(window.innerHeight / 3)
        
        // 如果数据已经加载，确保Interface选项已初始化
        if (operationLogs.value.length > 0 && operationInterfaceOptions.value.length === 0) {
          initOperationInterfaceOptions()
        }
      }, 100)
    })
  } else {
    // 当操作日志开关关闭时，清理数据
    operationLogs.value = []
    operationSearchText.value = ''
    debouncedOperationSearchText.value = ''
    interfaceFilters.value = [] // 清理interface筛选数据
    
    // 清理缓存
    const cacheKeysToDelete: string[] = []
    filteredLogsCache.value.forEach((_, key) => {
      if (key.startsWith('operation_logs_')) {
        cacheKeysToDelete.push(key)
      }
    })
    cacheKeysToDelete.forEach(key => filteredLogsCache.value.delete(key))
    
    // 当操作日志隐藏时，重置框架日志，因为DOM结构变化可能影响框架日志的滚动
    nextTick(() => {
      if (frameworkLogEnabled.value) {
        userScrolled.value.set('framework', false)
        scrollPositionMemory.value.delete('framework')
        safeUpdateFrameworkVirtualList()
      }
    })
    
    // 重置操作日志区域高度
    operationLogHeight.value = Math.floor(window.innerHeight / 3)
  }
})

// 监听告警日志开关变化
watch(alertLogEnabled, (newValue) => {
  console.log('告警日志开关状态变化:', newValue)
  
  if (newValue) {
    // 当告警日志开关打开时，加载告警日志
    console.log('告警日志开关打开，开始加载数据...')
    loadAlertLogs()
  } else {
    // 当告警日志开关关闭时，清理数据
    alertLogs.value = []
    alertLogColumns.value = []
    alertLogSearchText.value = ''
    console.log('告警日志开关关闭，清理数据')
  }
})

// 操作日志搜索防抖
let operationSearchDebounceTimer: ReturnType<typeof setTimeout> | null = null
watch(operationSearchText, (newValue) => {
  if (operationSearchDebounceTimer) {
    clearTimeout(operationSearchDebounceTimer)
  }
  
  operationSearchDebounceTimer = setTimeout(() => {
    debouncedOperationSearchText.value = newValue
  }, 300) // 300ms 防抖延迟
})

// 监听操作日志防抖搜索文本变化，清理缓存
watch(debouncedOperationSearchText, () => {
  // 操作日志搜索文本变化时清理缓存
  const cacheKeysToDelete: string[] = []
  filteredLogsCache.value.forEach((_, key) => {
    if (key.startsWith('operation_logs_')) {
      cacheKeysToDelete.push(key)
    }
  })
  cacheKeysToDelete.forEach(key => filteredLogsCache.value.delete(key))
  
  // 重置滚动位置状态，避免搜索后滚动位置异常
  nextTick(() => {
    userScrolled.value.set('operation', false)
    scrollPositionMemory.value.delete('operation')
    
    // 强制虚拟列表重新渲染，彻底解决滚动条消失问题
    safeUpdateOperationVirtualList()
    
    // 更新区域筛选状态
    updateAreaFilterStatus()
    
    // 更新Interface选项，确保与当前筛选条件一致
    updateOperationInterfaceOptions()
  })
})

// 框架日志相关函数
const frameworkLogs = ref<any[]>([])
const frameworkSearchText = ref<string>('')
const debouncedFrameworkSearchText = ref<string>('')
// 框架日志级别过滤
const frameworkLevelFilters = ref<Map<string, boolean>>(new Map())

function getFrameworkLogs(): any[] {
  if (!frameworkLogEnabled.value) {
    return []
  }
  
  // 应用级别过滤
  return frameworkLogs.value.filter(log => {
    const level = log.level || 'unknown'
    const isLevelEnabled = frameworkLevelFilters.value.get(level)
    // 如果没有设置过滤，则默认启用所有级别
    return isLevelEnabled === undefined || isLevelEnabled
  })
}

// 获取应用了搜索过滤的框架日志
function getFilteredFrameworkLogsForDisplay(): any[] {
  if (!frameworkLogEnabled.value) {
    return []
  }
  
  // 先应用级别过滤，再应用时间过滤和搜索过滤
  const levelFilteredLogs = getFrameworkLogs()
  return levelFilteredLogs.filter(log => {
    // 时间过滤
    if (!isLogInTimeRange(log.timestamp)) {
      return false
    }
    
    // 搜索过滤
    return isFrameworkLogMatchSearch(log)
  })
}

// 仅用于统计：忽略级别开关，只应用时间与搜索过滤，避免统计项消失
function getFrameworkLogsForStats(): any[] {
  if (!frameworkLogEnabled.value) {
    return []
  }
  // 基于原始框架日志，应用时间与搜索过滤（不应用级别过滤）
  const allLogs = frameworkLogs.value
  return allLogs.filter(log => {
    if (!isLogInTimeRange(log.timestamp)) return false
    return isFrameworkLogMatchSearch(log)
  })
}

function getFrameworkLogStats() {
  // 改为仅受时间与搜索影响，不受级别开关影响，避免行消失
  const logsForStats = getFrameworkLogsForStats()
  const stats = calculateFrameworkLogStats(logsForStats)
  return stats
}

function calculateFrameworkLogStats(logs: any[]) {
  const stats = new Map<string, number>()
  
  logs.forEach(log => {
    // 保持原始级别格式，特别是 'Skynet' 级别
    const level = log.level || 'unknown'
    if (!stats.has(level)) {
      stats.set(level, 0)
    }
    stats.set(level, stats.get(level)! + 1)
  })
  
  // 将统计结果转换为数组，并确保 'Skynet' 排在最后
  const statsArray = Array.from(stats.entries()).map(([level, total]) => ({
    level,
    total
  }))
  
  // 排序：将 'Skynet' 级别排在最后，其他级别按字母顺序排列
  statsArray.sort((a, b) => {
    if (a.level === 'Skynet') return 1
    if (b.level === 'Skynet') return -1
    return a.level.localeCompare(b.level)
  })
  
  return statsArray
}

function getFrameworkStatsColumns() {
  return [
    {
      title: 'Level',
      key: 'level',
      width: 120
    },
    {
      title: 'Total',
      key: 'total',
      width: 80
    },
    {
      title: 'Filter',
      key: 'filter',
      width: 80,
      render: (row: any) => {
        // 检查当前级别是否启用
        const isEnabled = frameworkLevelFilters.value.get(row.level)
        const isLevelEnabled = isEnabled === undefined || isEnabled
        
        return h(NSwitch, {
          value: isLevelEnabled,
          size: 'small',
          onUpdateValue: (value: boolean) => {
            toggleFrameworkLevelFilter(row.level, value)
          }
        })
      }
    }
  ]
}

  // 切换框架日志级别过滤
function toggleFrameworkLevelFilter(level: string, enabled: boolean) {
  
  if (enabled) {
    // 启用级别：删除过滤设置（回到默认启用状态）
    frameworkLevelFilters.value.delete(level)
  } else {
    // 禁用级别：设置为false
    frameworkLevelFilters.value.set(level, false)
  }
  
  
  // 强制触发响应式更新
  frameworkLevelFilters.value = new Map(frameworkLevelFilters.value)
  
  // 清理框架日志相关的缓存
  const cacheKeysToDelete: string[] = []
  filteredLogsCache.value.forEach((_, key) => {
    if (key.startsWith('framework_logs_')) {
      cacheKeysToDelete.push(key)
    }
  })
  cacheKeysToDelete.forEach(key => filteredLogsCache.value.delete(key))
  
  // 修复滚动位置问题：在过滤变化前保存当前滚动位置
  const frameworkContainer = document.querySelector('.framework-logs') as HTMLElement
  let currentScrollPosition = 0
  if (frameworkContainer) {
    currentScrollPosition = frameworkContainer.scrollTop
    // 保存当前滚动位置，避免过滤后丢失
    scrollPositionMemory.value.set('framework', currentScrollPosition)
  }
  
  // 延迟更新虚拟列表，确保滚动位置被正确保存
  nextTick(() => {
    // 保持用户当前的滚动状态，不要强制重置
    // userScrolled.value.set('framework', false) // 注释掉这行，避免强制重置
    
    // 强制虚拟列表重新渲染，但保持滚动位置
    safeUpdateFrameworkVirtualList()
  })
}

async function loadFrameworkLogs() {
  
  if (!currentWorkspace.value) {
    console.warn('没有工作区，无法加载框架日志')
    return
  }

  // 优先检查 hasWorkspaceData，但也提供容错机制
  if (!hasWorkspaceData.value) {
    console.log('loadFrameworkLogs: hasWorkspaceData 为 false，尝试直接获取数据')
  }

  const fileData = fileDataStore.getFileData(currentWorkspace.value.id)
  
  if (!fileData) {
    console.warn('没有文件数据，无法加载框架日志')
    return
  }

  const frameworkLogPath = LOG_PATHS.LOG_DUMP.FRAMEWORK

  try {
    // 1. 加载原始框架日志
    const parsedLogs: any[] = []
    const frameworkContent = extractFileContent(fileData, frameworkLogPath)
    if (frameworkContent) {
      // 解析原始框架日志内容
      const lines = frameworkContent.split('\n')
      for (const line of lines) {
        const trimmedLine = line.trim()
        if (trimmedLine) {
          const parsedLog = parseFrameworkLogLine(trimmedLine)
          if (parsedLog) {
            parsedLogs.push(parsedLog)
          }
        }
      }
    } else {
      console.warn('未找到原始框架日志文件')
    }
    
    // 2. 加载转储框架日志
    try {
      const dumpLogs = await getAllDumpLogs(currentWorkspace.value.id, 'framework')
      
      for (const dump of dumpLogs) {
        const lines = dump.data.split('\n')
        
        for (const line of lines) {
          const trimmedLine = line.trim()
          if (trimmedLine) {
            const parsedLog = parseFrameworkLogLine(trimmedLine)
            if (parsedLog) {
              // 标记这是来自转储文件的日志
              const logWithSource = { ...parsedLog, source: `dump-${dump.index}` }
              parsedLogs.push(logWithSource)
            }
          }
        }
      }
    } catch (error) {
      console.warn('加载框架转储日志失败:', error)
    }
    
    // 3. 按时间戳排序所有日志（原始 + 转储）
    parsedLogs.sort((a, b) => {
      const timeA = new Date(a.timestamp).getTime()
      const timeB = new Date(b.timestamp).getTime()
      return timeA - timeB
    })
    
    frameworkLogs.value = parsedLogs
    
    // 4. 强制清理缓存，确保新数据能正确显示
    const cacheKeysToDelete: string[] = []
    filteredLogsCache.value.forEach((_, key) => {
      if (key.startsWith('framework_logs_')) {
        cacheKeysToDelete.push(key)
      }
    })
    cacheKeysToDelete.forEach(key => filteredLogsCache.value.delete(key))
    
    // 5. 强制UI更新
    nextTick(() => {
      safeUpdateFrameworkVirtualList()
      
      // 强制触发统计数据的响应式更新
      frameworkLogs.value = [...frameworkLogs.value]
    })
    
  } catch (error) {
    console.error('读取框架日志失败:', error)
    frameworkLogs.value = []
  }
}

function parseFrameworkLogLine(line: string) {
  // 检查是否为Skynet日志格式: 1970-01-01 08:03:21.403498 [:00000008] framework: LAUNCH snlua hica/subsys/framework/service/main
  const skynetPattern = /^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}\.\d+)\s+(\[:00000008\]\s+framework:)\s+(.+)$/
  const skynetMatch = line.match(skynetPattern)
  
  if (skynetMatch) {
    return {
      timestamp: skynetMatch[1],
      component: '', // Skynet日志不显示component字段
      level: 'Skynet', // 统一归类为Skynet级别
      message: `${skynetMatch[2]} ${skynetMatch[3]}` // 保留完整的 [:00000008] framework: 信息
    }
  }
  
  // 检查是否为Skynet日志格式（通用版本）: 1970-01-01 08:03:21.403498 [:00000002] framework: LAUNCH snlua hica/subsys/framework/service/main
  const skynetPatternGeneral = /^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}\.\d+)\s+(\[:00000002\]\s+framework:)\s+(.+)$/
  const skynetMatchGeneral = line.match(skynetPatternGeneral)
  
  if (skynetMatchGeneral) {
    return {
      timestamp: skynetMatchGeneral[1],
      component: '', // Skynet日志不显示component字段
      level: 'Skynet', // 统一归类为Skynet级别
      message: `${skynetMatchGeneral[2]} ${skynetMatchGeneral[3]}` // 保留完整的 [:00000002] framework: 信息
    }
  }
  
  // 检查是否为Skynet日志格式（更通用版本）: 1970-01-01 08:03:21.403498 [:任意数字] framework: 任意内容
  const skynetPatternUniversal = /^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}\.\d+)\s+(\[:[0-9]+\]\s+framework:)\s+(.+)$/
  const skynetMatchUniversal = line.match(skynetPatternUniversal)
  
  if (skynetMatchUniversal) {
    return {
      timestamp: skynetMatchUniversal[1],
      component: '', // Skynet日志不显示component字段
      level: 'Skynet', // 统一归类为Skynet级别
      message: `${skynetMatchUniversal[2]} ${skynetMatchUniversal[3]}` // 保留完整的 [:任意数字] framework: 信息
    }
  }
  
  // 检查是否为组件框架日志格式: 1970-01-01 08:03:23.328862 unknown_service NOTICE: harbor.lua(30): start message queue harbor.bmc.kepler.framework receive loop, id = 1
  const componentPattern = /^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}\.\d+)\s+([a-zA-Z_][a-zA-Z0-9_]*)\s+([A-Z]+):\s*(.+)$/
  const componentMatch = line.match(componentPattern)
  
  if (componentMatch) {
    return {
      timestamp: componentMatch[1],
      component: componentMatch[2], // 提取组件/服务名称
      level: componentMatch[3], // 提取实际日志级别
      message: componentMatch[4] // 只保留消息内容，不包含组件信息
    }
  }
  
  return null
}

function getFilteredFrameworkLogs(): any[] {
  if (!selectedLogTime.value) return []
  
  try {
    const selectedDate = new Date(selectedLogTime.value)
    if (isNaN(selectedDate.getTime())) return []
    
    let startTime: Date
    let endTime: Date
    
    if (showTimeInSeconds.value) {
      // 获取选中时间的秒级时间范围（同一秒）
      startTime = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate(), 
                           selectedDate.getHours(), selectedDate.getMinutes(), selectedDate.getSeconds(), 0)
      endTime = new Date(startTime.getTime() + 999) // 同一秒的结束时间
    } else {
      // 获取选中时间的分钟级时间范围（同一分钟）
      startTime = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate(), 
                           selectedDate.getHours(), selectedDate.getMinutes(), 0, 0)
      endTime = new Date(startTime.getTime() + 59 * 1000 + 999) // 同一分钟的结束时间
    }
    
    return getFrameworkLogs().filter(log => {
      try {
        const logTime = new Date(log.timestamp).getTime()
        return logTime >= startTime.getTime() && logTime <= endTime.getTime()
      } catch (error) {
        return false
      }
    })
  } catch (error) {
    console.warn('过滤框架日志失败:', error)
    return []
  }
}

function isFrameworkLogMatchSearch(log: any): boolean {
  // 检查全局搜索
  const globalSearchText = debouncedSearchText.value
  if (globalSearchText && globalSearchText.trim() !== '') {
    const globalSearchLower = globalSearchText.toLowerCase().trim()
    const logText = `${log.timestamp} ${log.level} ${log.message}`.toLowerCase()
    if (!logText.includes(globalSearchLower)) {
      return false // 全局搜索不匹配，直接返回false
    }
  }
  
  // 检查排除关键词
  const logContent = `${log.timestamp} ${log.level} ${log.message}`.toLowerCase()
  for (const excludeKeyword of excludeKeywords.value) {
    if (excludeKeyword.trim() && logContent.includes(excludeKeyword.toLowerCase().trim())) {
      return false // 包含排除关键词，直接返回false
    }
  }
  
  // 检查框架日志专用搜索
  const searchText = debouncedFrameworkSearchText.value
  if (!searchText || searchText.trim() === '') {
    return true
  }

  const searchLower = searchText.toLowerCase().trim()
  const logText = `${log.timestamp} ${log.level} ${log.message}`.toLowerCase()
  
  return logText.includes(searchLower)
}

function onFrameworkSearchChange(value: string) {
  frameworkSearchText.value = value
}

function removeFrameworkLog() {
  frameworkLogEnabled.value = false
  frameworkLogs.value = []
  frameworkSearchText.value = ''
  debouncedFrameworkSearchText.value = ''
  // 重置级别过滤状态
  frameworkLevelFilters.value.clear()
  
  // 清理缓存
  const cacheKeysToDelete: string[] = []
  filteredLogsCache.value.forEach((_, key) => {
    if (key.startsWith('framework_logs_')) {
      cacheKeysToDelete.push(key)
    }
  })
  cacheKeysToDelete.forEach(key => filteredLogsCache.value.delete(key))
  
  // 清理滚动位置状态
  userScrolled.value.delete('framework')
  scrollPositionMemory.value.delete('framework')
  
  // 重置其他日志类型的虚拟列表，因为DOM结构变化
  nextTick(() => {
    if (operationLogEnabled.value) {
      safeUpdateOperationVirtualList()
    }
    if (frameworkLogEnabled.value) {
      safeUpdateFrameworkVirtualList()
    }
  })
}

function removeAlertLog() {
  alertLogEnabled.value = false
  alertLogs.value = []
  alertLogSearchText.value = ''
  
  // 重置其他日志类型的虚拟列表，因为DOM结构变化
  nextTick(() => {
    if (operationLogEnabled.value) {
      safeUpdateOperationVirtualList()
    }
    if (frameworkLogEnabled.value) {
      safeUpdateFrameworkVirtualList()
    }
  })
}

// 监听框架日志开关变化
watch(frameworkLogEnabled, (newValue) => {
  if (newValue) {
    // 当框架日志开关打开时，加载框架日志
    loadFrameworkLogs()
    
    // 修复：不要重置滚动位置状态，避免滚动位置跳转
    nextTick(() => {
      // 保持用户当前的滚动状态，不要强制重置
      // userScrolled.value.set('framework', false) // 注释掉这行，避免强制重置
      // scrollPositionMemory.value.delete('framework') // 注释掉这行，避免丢失滚动位置
      
      // 强制虚拟列表重新渲染，但保持滚动位置
      safeUpdateFrameworkVirtualList()
      
      // 自动聚焦到新增的框架日志面板
      scrollToLogPanel('framework')
      
      // 当框架日志显示时，也重置操作日志，因为DOM结构变化可能影响操作日志的滚动
      if (operationLogEnabled.value) {
        userScrolled.value.set('operation', false)
        scrollPositionMemory.value.delete('operation')
        safeUpdateOperationVirtualList()
      }
      
      // 初始化框架日志区域高度，设置为当前屏幕的1/3
      setTimeout(() => {
        frameworkLogHeight.value = Math.floor(window.innerHeight / 3)
      }, 100)
    })
  } else {
    // 当框架日志开关关闭时，清理数据
    frameworkLogs.value = []
    frameworkSearchText.value = ''
    debouncedFrameworkSearchText.value = ''
    // 重置级别过滤状态
    frameworkLevelFilters.value.clear()
    
    // 清理缓存
    const cacheKeysToDelete: string[] = []
    filteredLogsCache.value.forEach((_, key) => {
      if (key.startsWith('framework_logs_')) {
        cacheKeysToDelete.push(key)
      }
    })
    cacheKeysToDelete.forEach(key => filteredLogsCache.value.delete(key))
    
    // 当框架日志隐藏时，重置操作日志，因为DOM结构变化可能影响操作日志的滚动
    nextTick(() => {
      if (operationLogEnabled.value) {
        userScrolled.value.set('operation', false)
        scrollPositionMemory.value.delete('operation')
        safeUpdateOperationVirtualList()
      }
    })
    
    // 重置框架日志区域高度
    frameworkLogHeight.value = Math.floor(window.innerHeight / 3)
  }
})

// 框架日志搜索防抖
let frameworkSearchDebounceTimer: ReturnType<typeof setTimeout> | null = null
watch(frameworkSearchText, (newValue) => {
  if (frameworkSearchDebounceTimer) {
    clearTimeout(frameworkSearchDebounceTimer)
  }
  
  frameworkSearchDebounceTimer = setTimeout(() => {
    debouncedFrameworkSearchText.value = newValue
  }, 300) // 300ms 防抖延迟
})

// 监听框架日志防抖搜索文本变化，清理缓存
watch(debouncedFrameworkSearchText, () => {
  // 框架日志搜索文本变化时清理缓存
  const cacheKeysToDelete: string[] = []
  filteredLogsCache.value.forEach((_, key) => {
    if (key.startsWith('framework_logs_')) {
      cacheKeysToDelete.push(key)
    }
  })
  cacheKeysToDelete.forEach(key => filteredLogsCache.value.delete(key))
  
  // 重置滚动位置状态，避免搜索后滚动位置异常
  nextTick(() => {
    userScrolled.value.set('framework', false)
    scrollPositionMemory.value.delete('framework')
    
    // 强制虚拟列表重新渲染，彻底解决滚动条消失问题
    safeUpdateFrameworkVirtualList()
    
    // 更新区域筛选状态
    updateAreaFilterStatus()
  })
})



onMounted(async () => {
  
  // 检查全局初始化状态
  const isAppInitComplete = (window as any).__APP_INIT_COMPLETE__
  
  if (!isAppInitComplete) {
    
    // 监听全局初始化完成事件
    const handleAppInitialized = () => {
      window.removeEventListener('app-data-initialized', handleAppInitialized)
      loadDataAfterInit()
    }
    
    window.addEventListener('app-data-initialized', handleAppInitialized)
    
    // 设置超时保护，避免无限等待
    setTimeout(() => {
      if (!(window as any).__APP_INIT_COMPLETE__) {
        console.warn('全局初始化超时，强制开始加载数据')
        window.removeEventListener('app-data-initialized', handleAppInitialized)
        loadDataAfterInit()
      }
    }, 3000) // 3秒超时
    
    return
  }
  
  // 如果全局初始化已完成，直接加载数据
  loadDataAfterInit()
  
  // 初始化区域筛选状态
  nextTick(() => {
    updateAreaFilterStatus()
  })
})

// 抽取数据加载逻辑到单独函数
async function loadDataAfterInit() {
  
  // 确保文件数据存储已初始化
  try {
    await fileDataStore.initializeFromStorage()
    console.log('文件数据存储初始化完成')
  } catch (error) {
    console.error('文件数据存储初始化失败:', error)
  }
  
  // 预加载调试日志缓存，提高后续操作响应速度
  if (currentWorkspace.value) {
    const fileData = fileDataStore.getFileData(currentWorkspace.value.id)
    
    if (hasWorkspaceData.value && fileData) {
      // 数据已经可用，直接加载
      const runtimeContent = extractFileContent(fileData, LOG_PATHS.LOG_DUMP.APP)
      if (runtimeContent) {
        runtimeLogCache.value[currentWorkspace.value.id] = runtimeContent.split('\n')
      }

      // 预加载操作日志
      loadOperationLogs()
      
      // 初始化日志区域高度，基于实际内容高度
      nextTick(() => {
        setTimeout(() => {
          if (operationLogEnabled.value) {
            operationLogHeight.value = Math.floor(window.innerHeight / 3)
          }
          if (frameworkLogHeight.value) {
            frameworkLogHeight.value = Math.floor(window.innerHeight / 3)
          }
        }, 100) // 延迟100ms确保DOM完全渲染
      })
    } else {
      
      // 设置多次重试机制，确保页面切换后能正确加载数据
      let retryCount = 0
      const maxRetries = 10  // 增加重试次数
      const retryInterval = 300 // 300ms，增加间隔
      
      const retryLoad = () => {
        retryCount++
        
        const retryFileData = fileDataStore.getFileData(currentWorkspace.value!.id)
        const retryHasData = fileDataStore.hasFileData(currentWorkspace.value!.id)
        
        
        if (retryHasData && retryFileData) {
          
          const runtimeContent = extractFileContent(retryFileData, LOG_PATHS.LOG_DUMP.APP)
          if (runtimeContent) {
            runtimeLogCache.value[currentWorkspace.value!.id] = runtimeContent.split('\n')
          }
          
          // 清除组件选项缓存，强制重新计算
          const workspaceId = currentWorkspace.value!.id
          delete componentOptionsCache.value[workspaceId]
          
          // 预加载操作日志
          loadOperationLogs()
          
          // 初始化日志区域高度，基于实际内容高度
          nextTick(() => {
            setTimeout(() => {
              if (operationLogEnabled.value) {
                operationLogHeight.value = Math.floor(window.innerHeight / 3)
              }
              if (frameworkLogEnabled.value) {
                frameworkLogHeight.value = Math.floor(window.innerHeight / 3)
              }
            }, 100) // 延迟100ms确保DOM完全渲染
          })
          
          return
        }
        
        if (retryCount < maxRetries) {
          setTimeout(retryLoad, retryInterval)
        } else {
          console.warn('重试次数用尽，数据加载失败')
          console.warn('可能的原因：1. 文件数据未正确恢复 2. IndexedDB访问失败 3. 数据损坏')
        }
      }
      
      setTimeout(retryLoad, retryInterval)
    }
  } else {
    console.warn('没有当前工作区，无法加载数据')
  }
  
  // 添加键盘事件监听器，支持快捷键操作
  document.addEventListener('keydown', handleKeyboardShortcuts)
}

// 监听 hasWorkspaceData 变化，当数据恢复时重新加载
watch(hasWorkspaceData, (newValue, oldValue) => {
  
  if (newValue && !oldValue && currentWorkspace.value) {
    
    const workspaceId = currentWorkspace.value.id
    
    // 预加载调试日志缓存
    const fileData = fileDataStore.getFileData(workspaceId)
    if (fileData) {
      const runtimeContent = extractFileContent(fileData, LOG_PATHS.LOG_DUMP.APP)
      if (runtimeContent) {
        runtimeLogCache.value[workspaceId] = runtimeContent.split('\n')
      }
    }
    
    // 清除组件选项缓存，强制重新计算
    delete componentOptionsCache.value[workspaceId]
    
    // 如果有选中的组件，重新加载它们的日志
    if (selectedComponents.value.length > 0) {
      setTimeout(() => {
        selectedComponents.value.forEach(async (component) => {
          await loadComponentLogs(component)
        })
      }, 100)
    }
    
    // 重新加载操作日志和框架日志
    setTimeout(() => {
      loadOperationLogs()
      loadFrameworkLogs()
      
      // 重新初始化日志区域高度，基于实际内容高度
      nextTick(() => {
        setTimeout(() => {
          if (operationLogEnabled.value) {
            operationLogHeight.value = Math.floor(window.innerHeight / 3)
          }
          if (frameworkLogEnabled.value) {
            frameworkLogHeight.value = Math.floor(window.innerHeight / 3)
          }
          
          // 只为新组件初始化日志区域高度，保持已有组件的高度不变
          selectedComponents.value.forEach(component => {
            // 如果组件还没有设置高度，才设置初始高度
            if (!componentLogHeights.value.has(component)) {
              const componentHeight = Math.floor(window.innerHeight / 3)
              componentLogHeights.value.set(component, componentHeight)
            }
          })
        }, 100) // 延迟100ms确保DOM完全渲染
      })
    }, 200)
  }
}, { immediate: false })
  
  // 添加窗口大小变化监听器
  const handleResize = () => {
    currentWindowWidth.value = window.innerWidth
  }
  
  window.addEventListener('resize', handleResize)
  
  // 初始化滚动位置管理
  nextTick(() => {
    initScrollPositionManagement()
  })
  
  // 监听组件变化，重新初始化滚动位置管理
watch(selectedComponents, () => {
  nextTick(() => {
    initScrollPositionManagement()
    
    // 重置新组件的滚动位置状态
    selectedComponents.value.forEach((component, index) => {
      const componentName = `component-${index}`
      userScrolled.value.set(componentName, false)
      scrollPositionMemory.value.delete(componentName)
      
      // 强制虚拟列表重新渲染，彻底解决滚动条消失问题
      const currentKey = componentVirtualListKeys.value.get(component) || 0
      componentVirtualListKeys.value.set(component, currentKey + 1)
    })
    
    // 当组件日志显示/隐藏时，重置其他日志类型，因为DOM结构变化可能影响滚动
    if (frameworkLogEnabled.value) {
      userScrolled.value.set('framework', false)
      scrollPositionMemory.value.delete('framework')
      safeUpdateFrameworkVirtualList()
    }
    
    if (operationLogEnabled.value) {
      userScrolled.value.set('operation', false)
      scrollPositionMemory.value.delete('operation')
      safeUpdateOperationVirtualList()
    }
    
    // 只为新组件初始化日志区域高度，保持已有组件的高度不变
    setTimeout(() => {
      selectedComponents.value.forEach(component => {
        // 如果组件还没有设置高度，才设置初始高度
        if (!componentLogHeights.value.has(component)) {
          const componentHeight = Math.floor(window.innerHeight / 3)
          componentLogHeights.value.set(component, componentHeight)
        }
      })
    }, 100)
  })
})
  
// 组件卸载时清理事件监听器
onUnmounted(() => {
  document.removeEventListener('keydown', handleKeyboardShortcuts)
  window.removeEventListener('resize', handleResize)
  
  // 清理调试日志搜索防抖定时器
  if (debugLogSearchDebounceTimer) {
    clearTimeout(debugLogSearchDebounceTimer)
  }
})

// 智能滚动条增强：为大数据量场景优化滚动条体验
function enhanceScrollbarForLargeData(container: HTMLElement) {
  // 等待虚拟列表渲染完成
  nextTick(() => {
    const virtualList = container.querySelector('.n-virtual-list') as HTMLElement
    if (!virtualList) return

    // 监听滚动事件，动态调整滚动条样式
    const observer = new ResizeObserver(() => {
      adjustScrollbarForDataSize(virtualList)
    })
    
    observer.observe(virtualList)
    
    // 初始调整
    setTimeout(() => adjustScrollbarForDataSize(virtualList), 100)
  })
}

// 缓存当前的滚动条增强样式
let currentScrollbarEnhancementStyle: HTMLStyleElement | null = null

// 根据数据量调整滚动条最小高度
function adjustScrollbarForDataSize(virtualList: HTMLElement) {
  const scrollHeight = virtualList.scrollHeight
  const clientHeight = virtualList.clientHeight
  
  if (scrollHeight <= clientHeight) return // 无需滚动条
  
  // 计算滚动比例
  const scrollRatio = clientHeight / scrollHeight
  
  // 根据数据量动态计算最小滚动条高度和宽度
  let minThumbHeight = 120 // 默认最小高度
  let scrollbarWidth = 28 // 默认宽度
  let dataLevel = 'normal'
  
  if (scrollRatio < 0.01) { // 超大数据量（滚动条会很小）
    minThumbHeight = 220
    scrollbarWidth = 32
    dataLevel = 'huge'
  } else if (scrollRatio < 0.05) { // 大数据量
    minThumbHeight = 180
    scrollbarWidth = 30
    dataLevel = 'large'
  } else if (scrollRatio < 0.1) { // 中等数据量
    minThumbHeight = 150
    scrollbarWidth = 28
    dataLevel = 'medium'
  }
  
  // 移除旧的样式
  if (currentScrollbarEnhancementStyle) {
    currentScrollbarEnhancementStyle.remove()
  }
  
  // 创建新的动态样式
  const style = document.createElement('style')
  style.id = 'dynamic-scrollbar-enhancement'
  style.textContent = `
    /* 动态滚动条增强 - ${dataLevel} 数据量 */
    .log-content .n-virtual-list::-webkit-scrollbar {
      width: ${scrollbarWidth}px !important;
      height: ${scrollbarWidth}px !important;
    }
    
    .log-content .n-virtual-list::-webkit-scrollbar-thumb {
      min-height: ${minThumbHeight}px !important;
      /* 根据数据量调整透明度，提供视觉反馈 */
      background: linear-gradient(180deg, 
        rgba(208, 208, 208, ${dataLevel === 'huge' ? '0.9' : '0.8'}) 0%, 
        rgba(192, 192, 192, ${dataLevel === 'huge' ? '0.9' : '0.8'}) 50%, 
        rgba(176, 176, 176, ${dataLevel === 'huge' ? '0.9' : '0.8'}) 100%) !important;
    }
    
    .log-content .n-virtual-list::-webkit-scrollbar-thumb:hover {
      min-height: ${minThumbHeight + 20}px !important;
      background: linear-gradient(180deg, 
        rgba(184, 184, 184, ${dataLevel === 'huge' ? '1.0' : '0.9'}) 0%, 
        rgba(168, 168, 168, ${dataLevel === 'huge' ? '1.0' : '0.9'}) 50%, 
        rgba(152, 152, 152, ${dataLevel === 'huge' ? '1.0' : '0.9'}) 100%) !important;
    }
    
    .log-content .n-virtual-list::-webkit-scrollbar-thumb:active {
      min-height: ${minThumbHeight + 30}px !important;
      background: linear-gradient(180deg, 
        rgba(160, 160, 160, 1.0) 0%, 
        rgba(144, 144, 144, 1.0) 50%, 
        rgba(128, 128, 128, 1.0) 100%) !important;
    }
  `
  
  document.head.appendChild(style)
  currentScrollbarEnhancementStyle = style
}

// 滚动位置管理函数
function setupScrollPositionManagement(container: HTMLElement, componentName: string) {
  // 初始化该组件的滚动设置
  if (!autoScrollToBottom.value.has(componentName)) {
    // 修复：框架日志默认不自动滚动到底部，避免滚动位置跳转
    if (componentName === 'framework') {
      autoScrollToBottom.value.set(componentName, false)
    } else {
      autoScrollToBottom.value.set(componentName, true)
    }
  }
  if (!userScrolled.value.has(componentName)) {
    userScrolled.value.set(componentName, false)
  }

  // 智能滚动条增强：根据数据量动态调整滚动条
  enhanceScrollbarForLargeData(container)

  // 保存滚动位置
  function saveScrollPosition() {
    const scrollTop = container.scrollTop
    scrollPositionMemory.value.set(componentName, scrollTop)
    
    // 在拖拽调整大小期间，不要更新用户滚动状态，避免干扰拖拽逻辑
    if (isResizing.value) {
      return
    }
    
    // 检测用户是否手动滚动 - 修复：避免错误的用户滚动状态判断
    const previousPosition = scrollPositionMemory.value.get(componentName)
    if (previousPosition !== undefined && Math.abs(scrollTop - previousPosition) > 5) {
      // 只有在滚动位置确实发生变化时才标记为用户滚动
      userScrolled.value.set(componentName, true)
    }
  }

  // 恢复滚动位置 - 改进版本，避免与虚拟列表冲突
  function restoreScrollPosition() {
    const savedPosition = scrollPositionMemory.value.get(componentName)
    if (savedPosition !== undefined && !userScrolled.value.get(componentName)) {
      // 在拖拽调整大小期间，不要恢复滚动位置，避免干扰用户操作
      if (isResizing.value) {
        return
      }
      
      // 使用 nextTick 确保虚拟列表渲染完成后恢复位置
      nextTick(() => {
        // 检查容器是否仍然存在且有效
        if (container && container.scrollHeight > 0) {
          // 限制滚动位置在有效范围内
          const maxScrollTop = container.scrollHeight - container.clientHeight
          const targetPosition = Math.min(savedPosition, maxScrollTop)
          
          // 修复：只有在滚动位置确实不同时才恢复，避免不必要的跳转
          if (container.scrollTop !== targetPosition) {
            container.scrollTop = targetPosition
          }
        }
      })
    }
  }

  // 滚动到底部 - 改进版本
  function scrollToBottom() {
    // 在拖拽调整大小期间，禁用自动滚动，避免跳转到底部
    if (isResizing.value) {
      return
    }
    
    // 修复：框架日志默认不自动滚动到底部，避免滚动位置跳转
    if (componentName === 'framework' && !autoScrollToBottom.value.get(componentName)) {
      return
    }
    
    if (autoScrollToBottom.value.get(componentName) && !userScrolled.value.get(componentName)) {
      nextTick(() => {
        if (container && container.scrollHeight > 0) {
          // 修复：只有在用户没有手动滚动时才自动滚动到底部
          const currentScrollTop = container.scrollTop
          const maxScrollTop = container.scrollHeight - container.clientHeight
          
          // 如果用户已经接近底部（距离底部小于100px），则自动滚动到底部
          if (maxScrollTop - currentScrollTop < 100) {
            container.scrollTop = maxScrollTop
          }
        }
      })
    }
  }

  // 滚动到顶部
  function scrollToTop() {
    nextTick(() => {
      if (container) {
        container.scrollTop = 0
        userScrolled.value.set(componentName, false)
      }
    })
  }

  // 滚动条点击跳转功能 - 改进版本
  function setupScrollbarClickJump() {
    // 等待滚动条渲染完成
    setTimeout(() => {
      const scrollbarTrack = container.querySelector('.n-virtual-list::-webkit-scrollbar-track') || 
                             container.querySelector('.n-virtual-list::-webkit-scrollbar-track') ||
                             container.querySelector('.log-content::-webkit-scrollbar-track')
      
      if (scrollbarTrack) {
        scrollbarTrack.addEventListener('click', (event: Event) => {
          if (event instanceof MouseEvent) {
            handleScrollbarClick(event, container, componentName)
          }
        })
      }
      
      // 为整个容器添加点击事件，检测滚动条区域
      container.addEventListener('click', (event: Event) => {
        if (event instanceof MouseEvent) {
          handleContainerClick(event, container, componentName)
        }
      })
    }, 100)
  }

  // 处理滚动条点击
  function handleScrollbarClick(event: MouseEvent, container: HTMLElement, componentName: string) {
    event.preventDefault()
    event.stopPropagation()
    
    const rect = container.getBoundingClientRect()
    const clickY = event.clientY - rect.top
    const containerHeight = rect.height
    const scrollHeight = container.scrollHeight
    const clientHeight = container.clientHeight
    
    // 计算点击位置对应的滚动位置
    const scrollRatio = clickY / containerHeight
    const targetScrollTop = scrollRatio * (scrollHeight - clientHeight)
    
    // 跳转到目标位置
    container.scrollTop = targetScrollTop
    userScrolled.value.set(componentName, true)
    
  }

  // 处理容器点击（检测滚动条区域）
  function handleContainerClick(event: MouseEvent, container: HTMLElement, componentName: string) {
    const rect = container.getBoundingClientRect()
    const clickX = event.clientX - rect.left
    const containerWidth = rect.width
    
    // 检测是否点击在右侧滚动条区域（距离右边缘24px内）
    if (clickX > containerWidth - 24) {
      const clickY = event.clientY - rect.top
      const containerHeight = rect.height
      const scrollHeight = container.scrollHeight
      const clientHeight = container.clientHeight
      
      // 计算点击位置对应的滚动位置
      const scrollRatio = clickY / containerHeight
      const targetScrollTop = scrollRatio * (scrollHeight - clientHeight)
      
      // 跳转到目标位置
      container.scrollTop = targetScrollTop
      userScrolled.value.set(componentName, true)
      
    }
  }

  // 添加滚动事件监听器
  container.addEventListener('scroll', saveScrollPosition)

  // 添加右键菜单事件监听器
  container.addEventListener('contextmenu', (event) => {
    // 检查是否点击在日志项上，如果是则不显示滚动控制菜单
    const target = event.target as HTMLElement
    const isLogItem = target.closest('.log-item') || target.closest('.log-line') || target.closest('.log-time') || target.closest('.log-message')
    
    if (!isLogItem) {
      event.preventDefault()
      showScrollControlMenu(event, componentName)
    }
  })

  // 设置滚动条点击跳转
  setupScrollbarClickJump()

  // 返回控制函数
  return {
    saveScrollPosition,
    restoreScrollPosition,
    scrollToBottom,
    scrollToTop
  }
}

// 显示滚动控制菜单
function showScrollControlMenu(event: MouseEvent, componentName: string) {
  event.preventDefault()
  scrollControlMenuX.value = event.clientX
  scrollControlMenuY.value = event.clientY
  scrollControlMenuTarget.value = componentName
  scrollControlMenuShow.value = true
}

// 隐藏滚动控制菜单
function hideScrollControlMenu() {
  scrollControlMenuShow.value = false
  scrollControlMenuTarget.value = ''
}

// 处理滚动控制菜单选择
function handleScrollControlMenuSelect(key: string) {
  const componentName = scrollControlMenuTarget.value
  if (!componentName) return

  const container = document.querySelector(`[data-scroll-container="${componentName}"]`) as HTMLElement
  if (!container) return

  switch (key) {
    case 'scroll-to-top':
      container.scrollTop = 0
      userScrolled.value.set(componentName, false)
      break
    case 'scroll-to-bottom':
      container.scrollTop = container.scrollHeight
      userScrolled.value.set(componentName, false)
      break
    case 'restore-position':
      const savedPosition = scrollPositionMemory.value.get(componentName)
      if (savedPosition !== undefined) {
        container.scrollTop = savedPosition
        userScrolled.value.set(componentName, false)
      }
      break
    case 'toggle-auto-scroll':
      const currentAutoScroll = autoScrollToBottom.value.get(componentName) || false
      autoScrollToBottom.value.set(componentName, !currentAutoScroll)
      break
  }
  
  hideScrollControlMenu()
}

// 初始化滚动位置管理
function initScrollPositionManagement() {
  // 为框架日志设置滚动位置管理
  const frameworkContainer = document.querySelector('.framework-logs') as HTMLElement
  if (frameworkContainer) {
    frameworkContainer.setAttribute('data-scroll-container', 'framework')
    setupScrollPositionManagement(frameworkContainer, 'framework')
  }

  // 为操作日志设置滚动位置管理
  const operationContainer = document.querySelector('.operation-logs') as HTMLElement
  if (operationContainer) {
    operationContainer.setAttribute('data-scroll-container', 'operation')
    setupScrollPositionManagement(operationContainer, 'operation')
  }

  // 为调试日志组件设置滚动位置管理
  const componentContainers = document.querySelectorAll('.component-logs')
  componentContainers.forEach((container, index) => {
    const componentName = `component-${index}`
    container.setAttribute('data-scroll-container', componentName)
    setupScrollPositionManagement(container as HTMLElement, componentName)
  })
}

// 键盘快捷键处理
function handleKeyboardShortcuts(event: KeyboardEvent) {
  // Ctrl+C 复制选中的文本
  if (event.ctrlKey && event.key === 'c') {
    const selection = window.getSelection()
    if (selection && selection.toString().trim()) {
      // 如果有选中的文本，复制到剪贴板
      const selectedText = selection.toString()
      if (navigator.clipboard && navigator.clipboard.writeText) {
        navigator.clipboard.writeText(selectedText).then(() => {
        }).catch(err => {
          console.error('复制失败:', err)
          fallbackCopyText(selectedText)
        })
      } else {
        fallbackCopyText(selectedText)
      }
    }
  }
}

// 右键上下文菜单相关方法
function showContextMenu(event: MouseEvent, logItem: any, logType: string, component?: string) {
  event.preventDefault()
  
  // 设置菜单位置
  contextMenuX.value = event.clientX
  contextMenuY.value = event.clientY
  
  // 保存当前选中的日志项信息
  contextMenuLogItem.value = logItem
  contextMenuLogType.value = logType
  contextMenuComponent.value = component || ''
  
  // 显示菜单
  contextMenuShow.value = true
}

function hideContextMenu() {
  contextMenuShow.value = false
  contextMenuLogItem.value = null
  contextMenuLogType.value = ''
  contextMenuComponent.value = ''
}

function handleContextMenuSelect(key: string) {
  if (key === 'analyze-time-range') {
    // 打开时间分析抽屉
    if (contextMenuLogItem.value) {
      openTimeAnalysisDrawer(contextMenuLogItem.value)
    }
  }
  
  // 隐藏菜单
  hideContextMenu()
}

function analyzeTimeRange() {
  if (!contextMenuLogItem.value) return
  
  const logItem = contextMenuLogItem.value
  const logType = contextMenuLogType.value
  const component = contextMenuComponent.value
  
  // 解析日志时间戳
  const timestamp = logItem.timestamp
  if (!timestamp) {
    console.warn('日志项没有时间戳')
    return
  }
  
  // 将时间戳转换为Date对象
  const logDate = new Date(timestamp)
  if (isNaN(logDate.getTime())) {
    console.warn('无法解析时间戳:', timestamp)
    return
  }
  
  // 设置时间范围（前后各30分钟）
  const startDateTime = new Date(logDate.getTime() - 30 * 60 * 1000)
  const endDateTime = new Date(logDate.getTime() + 30 * 60 * 1000)
  
  // 更新全局时间筛选器
  startTime.value = startDateTime.getTime()
  endTime.value = endDateTime.getTime()
  
  // 更新区域筛选状态
  nextTick(() => {
    updateAreaFilterStatus()
  })
}

// 时间分析抽屉相关方法
function openTimeAnalysisDrawer(logItem: any) {
  if (!logItem || !logItem.timestamp) {
    console.warn('日志项没有时间戳')
    return
  }
  
  // 解析日志时间戳
  const timestamp = logItem.timestamp
  const logDate = new Date(timestamp)
  
  if (isNaN(logDate.getTime())) {
    console.warn('无法解析时间戳:', timestamp)
    return
  }
  
  // 每次打开抽屉时，默认设置为按秒分析
  showTimeInSeconds.value = true
  
  // 重置向前和向后范围为默认值
  forwardRange.value = null
  backwardRange.value = null
  
  // 设置时间分析抽屉的数据 - 只显示到秒，不显示毫秒
  selectedLogTime.value = formatTimeForDisplay(logDate)
  suggestedStartTime.value = formatTimeForDisplay(new Date(logDate.getTime() - 30 * 60 * 1000))
  suggestedEndTime.value = formatTimeForDisplay(new Date(logDate.getTime() + 30 * 60 * 1000))

  
  // 显示抽屉
  timeAnalysisDrawerShow.value = true
}

function closeTimeAnalysisDrawer() {
  timeAnalysisDrawerShow.value = false
  // 清理数据
  selectedLogTime.value = ''
  suggestedStartTime.value = ''
  suggestedEndTime.value = ''
}



// 时间导航方法
function goToPreviousSecond() {
  if (!selectedLogTime.value) return
  
  const currentDate = new Date(selectedLogTime.value)
  if (isNaN(currentDate.getTime())) return
  
  let previousTime: Date
  
  if (showTimeInSeconds.value) {
    // 切换到上一秒
    previousTime = new Date(currentDate.getTime() - 1000)
  } else {
    // 切换到上一分钟
    previousTime = new Date(currentDate.getTime() - 60 * 1000)
  }
  
  selectedLogTime.value = formatTimeForDisplay(previousTime)
  
  // 更新时间范围建议
  suggestedStartTime.value = formatTimeForDisplay(new Date(previousTime.getTime() - 30 * 60 * 1000))
  suggestedEndTime.value = formatTimeForDisplay(new Date(previousTime.getTime() + 30 * 60 * 1000))
}

function goToNextSecond() {
  if (!selectedLogTime.value) return
  
  const currentDate = new Date(selectedLogTime.value)
  if (isNaN(currentDate.getTime())) return
  
  let nextTime: Date
  
  if (showTimeInSeconds.value) {
    // 切换到下一秒
    nextTime = new Date(currentDate.getTime() + 1000)
  } else {
    // 切换到下一分钟
    nextTime = new Date(currentDate.getTime() + 60 * 1000)
  }
  
  selectedLogTime.value = formatTimeForDisplay(nextTime)
  
  // 更新时间范围建议
  suggestedStartTime.value = formatTimeForDisplay(new Date(nextTime.getTime() - 30 * 60 * 1000))
  suggestedEndTime.value = formatTimeForDisplay(new Date(nextTime.getTime() + 30 * 60 * 1000))
}

// 时间编辑相关方法
function startEditTime() {
  isEditingTime.value = true
  editingTimeValue.value = selectedLogTime.value
  // 使用nextTick确保DOM更新后再聚焦输入框
  nextTick(() => {
    if (timeInputRef.value) {
      timeInputRef.value.focus()
      // 选中所有文本
      timeInputRef.value.select()
    }
  })
}

function saveTimeEdit() {
  if (editingTimeValue.value.trim()) {
    // 验证时间格式
    const newDate = new Date(editingTimeValue.value)
    if (!isNaN(newDate.getTime())) {
      selectedLogTime.value = editingTimeValue.value.trim()
    } else {
      console.warn('无效的时间格式，保持原值')
      editingTimeValue.value = selectedLogTime.value
    }
  } else {
    // 如果输入为空，恢复原值
    editingTimeValue.value = selectedLogTime.value
  }
  isEditingTime.value = false
}

function cancelTimeEdit() {
  editingTimeValue.value = selectedLogTime.value
  isEditingTime.value = false
}

// 时间格式切换处理
function onTimeFormatChange(value: boolean) {
  showTimeInSeconds.value = value
  // 如果当前有选中的时间，重新格式化显示
  if (selectedLogTime.value) {
    const currentDate = new Date(selectedLogTime.value)
    if (!isNaN(currentDate.getTime())) {
      selectedLogTime.value = formatTimeForDisplay(currentDate)
    }
  }
}

// 根据当前设置格式化时间显示
function formatTimeForDisplay(date: Date): string {
  if (showTimeInSeconds.value) {
    // 按秒显示
    return date.toLocaleString('zh-CN', {
      year: 'numeric',
      month: '2-digit',
      day: '2-digit',
      hour: '2-digit',
      minute: '2-digit',
      second: '2-digit',
      hour12: false
    })
  } else {
    // 按分钟显示
    return date.toLocaleString('zh-CN', {
      year: 'numeric',
      month: '2-digit',
      day: '2-digit',
      hour: '2-digit',
      minute: '2-digit',
      hour12: false
    })
  }
}

// 文本选择和复制相关方法
function selectLogText(event: MouseEvent, logItem: any, logType: string, component?: string, field?: string) {
  const target = event.target as HTMLElement
  if (target && target.classList.contains('selectable-text')) {
    // 创建文本选择范围
    const range = document.createRange()
    range.selectNodeContents(target)
    
    // 清除现有选择并设置新选择
    const selection = window.getSelection()
    if (selection) {
      selection.removeAllRanges()
      selection.addRange(range)
    }
    
  }
}

function copyLogText(logItem: any, logType: string, component?: string, field?: string) {
  let logContent = ''
  
  // 如果指定了字段，只复制该字段的内容
  if (field === 'timestamp') {
    logContent = logItem.timestamp
  } else {
    // 根据日志类型构建复制内容
    switch (logType) {
              case 'operation':
          logContent = `${logItem.timestamp} ${logItem.interface} ${logItem.user},${logItem.component},${logItem.message}`
          break
      case 'framework':
        logContent = `${logItem.timestamp} ${logItem.level} ${logItem.message}`
        break
      case 'runtime':
        logContent = `${logItem.timestamp} ${logItem.component} ${logItem.level} ${logItem.message}`
        break
      default:
        logContent = JSON.stringify(logItem, null, 2)
    }
  }
  
  // 使用现代 Clipboard API 复制文本
  if (navigator.clipboard && navigator.clipboard.writeText) {
    navigator.clipboard.writeText(logContent).then(() => {
      // 可以在这里添加成功提示
    }).catch(err => {
      console.error('复制失败:', err)
      // 降级到传统方法
      fallbackCopyText(logContent)
    })
  } else {
    // 降级到传统方法
    fallbackCopyText(logContent)
  }
}

function fallbackCopyText(text: string) {
  // 创建临时文本区域元素
  const textArea = document.createElement('textarea')
  textArea.value = text
  textArea.style.position = 'fixed'
  textArea.style.left = '-999999px'
  textArea.style.top = '-999999px'
  document.body.appendChild(textArea)
  
  // 选择并复制文本
  textArea.focus()
  textArea.select()
  
  try {
    const successful = document.execCommand('copy')
    if (successful) {
      console.log('日志内容已复制到剪贴板 (降级方法):', text)
    } else {
      console.error('复制失败 (降级方法)')
    }
  } catch (err) {
    console.error('复制失败 (降级方法):', err)
  }
  
  // 清理临时元素
  document.body.removeChild(textArea)
}

function getTimeFilteredOperationLogs() {
  if (!selectedLogTime.value) return []
  
  try {
    const selectedDate = new Date(selectedLogTime.value)
    if (isNaN(selectedDate.getTime())) return []
    
    let startTime: Date
    let endTime: Date
    
    if (showTimeInSeconds.value) {
      if (getForwardRangeValue() === 0 && getBackwardRangeValue() === 0) {
        // 当向前和向后范围都为0时，显示整个秒的数据
        startTime = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate(), 
                           selectedDate.getHours(), selectedDate.getMinutes(), selectedDate.getSeconds(), 0)
        endTime = new Date(startTime.getTime() + 999) // 同一秒的结束时间
      } else {
        // 根据向前和向后范围计算秒级时间范围（闭区间）
        const forwardSeconds = getForwardRangeValue() * 1000
        const backwardSeconds = getBackwardRangeValue() * 1000
        
        // 计算开始时间：向前范围，确保包含整个时间单位
        if (getForwardRangeValue() === 0) {
          // 如果向前范围为0，开始时间应该是选中时间点的开始（即选中秒的000毫秒）
          startTime = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate(), 
                           selectedDate.getHours(), selectedDate.getMinutes(), selectedDate.getSeconds(), 0)
        } else {
          // 如果向前范围大于0，计算到前N秒的开始边界
          const targetSecond = selectedDate.getSeconds() - getForwardRangeValue()
          startTime = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate(), 
                           selectedDate.getHours(), selectedDate.getMinutes(), targetSecond, 0)
        }
        
        // 计算结束时间：向后范围，确保包含整个时间单位
        if (getBackwardRangeValue() === 0) {
          // 如果向后范围为0，结束时间应该是选中时间点的结束（即选中秒的999毫秒）
          endTime = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate(), 
                           selectedDate.getHours(), selectedDate.getMinutes(), selectedDate.getSeconds(), 999)
        } else {
          // 如果向后范围大于0，计算到后N秒的结束边界
          const targetSecond = selectedDate.getSeconds() + getBackwardRangeValue()
          endTime = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate(), 
                           selectedDate.getHours(), selectedDate.getMinutes(), targetSecond, 999)
        }
      }
    } else {
      if (getForwardRangeValue() === 0 && getBackwardRangeValue() === 0) {
        // 当向前和向后范围都为0时，显示整个分钟的数据
        startTime = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate(), 
                           selectedDate.getHours(), selectedDate.getMinutes(), 0, 0)
        endTime = new Date(startTime.getTime() + 59 * 1000 + 999) // 同一分钟的结束时间
      } else {
        // 根据向前和向后范围计算分钟级时间范围（闭区间）
        const forwardMinutes = getForwardRangeValue() * 60 * 1000
        const backwardMinutes = getBackwardRangeValue() * 60 * 1000
        
        // 计算开始时间：向前范围，确保包含整个时间单位
        if (getForwardRangeValue() === 0) {
          // 如果向前范围为0，开始时间应该是选中分钟的00秒000毫秒
          startTime = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate(), 
                           selectedDate.getHours(), selectedDate.getMinutes(), 0, 0)
        } else {
          // 如果向前范围大于0，计算到前N分钟的开始边界
          const targetMinute = selectedDate.getMinutes() - getForwardRangeValue()
          startTime = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate(), 
                           selectedDate.getHours(), targetMinute, 0, 0)
        }
        
        // 计算结束时间：向后范围，确保包含整个时间单位
        if (getBackwardRangeValue() === 0) {
          // 如果向后范围为0，结束时间应该是选中分钟的59秒999毫秒
          endTime = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate(), 
                           selectedDate.getHours(), selectedDate.getMinutes(), 59, 999)
        } else {
          // 如果向后范围大于0，计算到后N分钟的结束边界
          const targetMinute = selectedDate.getMinutes() + getBackwardRangeValue()
          endTime = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate(), 
                           selectedDate.getHours(), targetMinute, 59, 999)
        }
      }
    }
    
    return getOperationLogs().filter(log => {
      try {
        const logTime = new Date(log.timestamp).getTime()
        return logTime >= startTime.getTime() && logTime <= endTime.getTime()
      } catch (error) {
        return false
      }
    })
  } catch (error) {
    console.warn('过滤操作日志失败:', error)
    return []
  }
}

function getTimeFilteredFrameworkLogs() {
  if (!selectedLogTime.value) return []
  
  try {
    const selectedDate = new Date(selectedLogTime.value)
    if (isNaN(selectedDate.getTime())) return []
    
    let startTime: Date
    let endTime: Date
    
    if (showTimeInSeconds.value) {
      if (getForwardRangeValue() === 0 && getBackwardRangeValue() === 0) {
        // 当向前和向后范围都为0时，显示整个秒的数据
        startTime = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate(), 
                           selectedDate.getHours(), selectedDate.getMinutes(), selectedDate.getSeconds(), 0)
        endTime = new Date(startTime.getTime() + 999) // 同一秒的结束时间
      } else {
        // 根据向前和向后范围计算秒级时间范围（闭区间）
        const forwardSeconds = getForwardRangeValue() * 1000
        const backwardSeconds = getBackwardRangeValue() * 1000
        
        // 计算开始时间：向前范围，确保包含整个时间单位
        if (getForwardRangeValue() === 0) {
          // 如果向前范围为0，开始时间应该是选中时间点的开始（即选中秒的000毫秒）
          startTime = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate(), 
                           selectedDate.getHours(), selectedDate.getMinutes(), selectedDate.getSeconds(), 0)
        } else {
          // 如果向前范围大于0，计算到前N秒的开始边界
          const targetSecond = selectedDate.getSeconds() - getForwardRangeValue()
          startTime = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate(), 
                           selectedDate.getHours(), selectedDate.getMinutes(), targetSecond, 0)
        }
        
        // 计算结束时间：向后范围，确保包含整个时间单位
        if (getBackwardRangeValue() === 0) {
          // 如果向后范围为0，结束时间应该是选中时间点的结束（即选中秒的999毫秒）
          endTime = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate(), 
                           selectedDate.getHours(), selectedDate.getMinutes(), selectedDate.getSeconds(), 999)
        } else {
          // 如果向后范围大于0，计算到后N秒的结束边界
          const targetSecond = selectedDate.getSeconds() + getBackwardRangeValue()
          endTime = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate(), 
                           selectedDate.getHours(), selectedDate.getMinutes(), targetSecond, 999)
        }
      }
    } else {
      if (getForwardRangeValue() === 0 && getBackwardRangeValue() === 0) {
        // 当向前和向后范围都为0时，显示整个分钟的数据
        startTime = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate(), 
                           selectedDate.getHours(), selectedDate.getMinutes(), 0, 0)
        endTime = new Date(startTime.getTime() + 59 * 1000 + 999) // 同一分钟的结束时间
      } else {
        // 根据向前和向后范围计算分钟级时间范围（闭区间）
        const forwardMinutes = getForwardRangeValue() * 60 * 1000
        const backwardMinutes = getBackwardRangeValue() * 60 * 1000
        
        // 计算开始时间：向前范围，确保包含整个时间单位
        if (getForwardRangeValue() === 0) {
          // 如果向前范围为0，开始时间应该是选中分钟的00秒000毫秒
          startTime = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate(), 
                           selectedDate.getHours(), selectedDate.getMinutes(), 0, 0)
        } else {
          // 如果向前范围大于0，计算到前N分钟的开始边界
          const targetMinute = selectedDate.getMinutes() - getForwardRangeValue()
          startTime = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate(), 
                           selectedDate.getHours(), targetMinute, 0, 0)
        }
        
        // 计算结束时间：向后范围，确保包含整个时间单位
        if (getBackwardRangeValue() === 0) {
          // 如果向后范围为0，结束时间应该是选中分钟的59秒999毫秒
          endTime = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate(), 
                           selectedDate.getHours(), selectedDate.getMinutes(), 59, 999)
        } else {
          // 如果向后范围大于0，计算到后N分钟的结束边界
          const targetMinute = selectedDate.getMinutes() + getBackwardRangeValue()
          endTime = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate(), 
                           selectedDate.getHours(), targetMinute, 59, 999)
        }
      }
    }
    
    // 直接从框架日志数据中按时间过滤，避免重复过滤
    return frameworkLogs.value.filter(log => {
      try {
        const logTime = new Date(log.timestamp).getTime()
        return logTime >= startTime.getTime() && logTime <= endTime.getTime()
      } catch (error) {
        return false
      }
    })
  } catch (error) {
    console.warn('过滤框架日志失败:', error)
    return []
  }
}

function getTimeFilteredRuntimeLogs() {
  if (!selectedLogTime.value) return []
  
  try {
    const selectedDate = new Date(selectedLogTime.value)
    if (isNaN(selectedDate.getTime())) return []
    
    let startTime: Date
    let endTime: Date
    
    if (showTimeInSeconds.value) {
      if (getForwardRangeValue() === 0 && getBackwardRangeValue() === 0) {
        // 当向前和向后范围都为0时，显示整个秒的数据
        startTime = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate(), 
                           selectedDate.getHours(), selectedDate.getMinutes(), selectedDate.getSeconds(), 0)
        endTime = new Date(startTime.getTime() + 999) // 同一秒的结束时间
      } else {
        // 根据向前和向后范围计算秒级时间范围（闭区间）
        const forwardSeconds = getForwardRangeValue() * 1000
        const backwardSeconds = getBackwardRangeValue() * 1000
        
        // 计算开始时间：向前范围，确保包含整个时间单位
        if (getForwardRangeValue() === 0) {
          // 如果向前范围为0，开始时间应该是选中时间点的开始（即选中秒的000毫秒）
          startTime = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate(), 
                           selectedDate.getHours(), selectedDate.getMinutes(), selectedDate.getSeconds(), 0)
        } else {
          // 如果向前范围大于0，计算到前N秒的开始边界
          const targetSecond = selectedDate.getSeconds() - getForwardRangeValue()
          startTime = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate(), 
                           selectedDate.getHours(), selectedDate.getMinutes(), targetSecond, 0)
        }
        
        // 计算结束时间：向后范围，确保包含整个时间单位
        if (getBackwardRangeValue() === 0) {
          // 如果向后范围为0，结束时间应该是选中时间点的结束（即选中秒的999毫秒）
          endTime = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate(), 
                           selectedDate.getHours(), selectedDate.getMinutes(), selectedDate.getSeconds(), 999)
        } else {
          // 如果向后范围大于0，计算到后N秒的结束边界
          const targetSecond = selectedDate.getSeconds() + getBackwardRangeValue()
          endTime = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate(), 
                           selectedDate.getHours(), selectedDate.getMinutes(), targetSecond, 999)
        }
      }
    } else {
      if (getForwardRangeValue() === 0 && getBackwardRangeValue() === 0) {
        // 当向前和向后范围都为0时，显示整个分钟的数据
        startTime = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate(), 
                           selectedDate.getHours(), selectedDate.getMinutes(), 0, 0)
        endTime = new Date(startTime.getTime() + 59 * 1000 + 999) // 同一分钟的结束时间
      } else {
        // 根据向前和向后范围计算分钟级时间范围（闭区间）
        const forwardMinutes = getForwardRangeValue() * 60 * 1000
        const backwardMinutes = getBackwardRangeValue() * 60 * 1000
        
        // 计算开始时间：向前范围，确保包含整个时间单位
        if (getForwardRangeValue() === 0) {
          // 如果向前范围为0，开始时间应该是选中分钟的00秒000毫秒
          startTime = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate(), 
                           selectedDate.getHours(), selectedDate.getMinutes(), 0, 0)
        } else {
          // 如果向前范围大于0，计算到前N分钟的开始边界
          const targetMinute = selectedDate.getMinutes() - getForwardRangeValue()
          startTime = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate(), 
                           selectedDate.getHours(), targetMinute, 0, 0)
        }
        
        // 计算结束时间：向后范围，确保包含整个时间单位
        if (getBackwardRangeValue() === 0) {
          // 如果向后范围为0，结束时间应该是选中分钟的59秒999毫秒
          endTime = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate(), 
                           selectedDate.getHours(), selectedDate.getMinutes(), 59, 999)
        } else {
          // 如果向后范围大于0，计算到后N分钟的结束边界
          const targetMinute = selectedDate.getMinutes() + getBackwardRangeValue()
          endTime = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate(), 
                           selectedDate.getHours(), targetMinute, 59, 999)
        }
      }
    }
    
    // 直接从调试日志缓存中按时间过滤，不遍历组件
    const workspaceId = currentWorkspace.value?.id
    if (!workspaceId) return []
    
    let lines = runtimeLogCache.value[workspaceId]
    if (!lines) {
      console.log('getTimeRangeFilteredLogs: 没有缓存的调试日志，尝试重新加载')
      
      // 尝试直接从文件数据中提取
      const fileData = fileDataStore.getFileData(workspaceId)
      if (fileData) {
        const runtimeContent = extractFileContent(fileData, LOG_PATHS.LOG_DUMP.APP)
        if (runtimeContent) {
          lines = runtimeContent.split('\n')
          runtimeLogCache.value[workspaceId] = lines
        } else {
          console.log('getTimeRangeFilteredLogs: 无法提取运行时内容')
          return []
        }
      } else {
        console.log('getTimeRangeFilteredLogs: 无法获取文件数据')
        return []
      }
    }
    const timeFilteredLogs: any[] = []
    
    for (let i = 0; i < lines.length; i++) {
      const line = lines[i].trim()
      if (line) {
        const parsedLog = parseRuntimeLogLine(line)
        if (parsedLog && parsedLog.timestamp) {
          try {
            const logTime = new Date(parsedLog.timestamp).getTime()
            if (logTime >= startTime.getTime() && logTime <= endTime.getTime()) {
              timeFilteredLogs.push({
                timestamp: parsedLog.timestamp,
                component: parsedLog.component,
                level: parsedLog.level,
                message: parsedLog.message
              })
            }
          } catch (error) {
            // 忽略时间解析错误的日志
            continue
          }
        }
      }
    }
    
    return timeFilteredLogs
  } catch (error) {
    console.warn('过滤调试日志失败:', error)
    return []
  }
}

// 处理日志拖拽开始
function handleLogDragStart(event: DragEvent, logItem: any, logType: string, component?: string) {
  if (!event.dataTransfer) return
  
  // 构建完整的原始日志内容
  let rawContent = logItem.message
  if (logType === 'operation' && logItem.interface && logItem.user && logItem.component) {
    // 操作日志格式：时间 接口,用户,组件,消息
            rawContent = `${logItem.timestamp} ${logItem.interface},${logItem.user},${logItem.component},${logItem.message}`
  }
  
  // 设置拖拽数据
  const dragData = {
    timestamp: logItem.timestamp,
    level: logItem.level || 'INFO',
    source: logItem.source || logItem.component || logItem.interface || 'unknown',
    message: logItem.message,
    filePath: 'unknown',
    lineNumber: undefined,
    rawContent: rawContent, // 使用完整的原始日志内容
    logType: logType,
    component: component || logItem.component,
    userInfo: logItem.user, // 添加用户信息字段
    logIndex: logItem.index || logItem.lineNumber || undefined  // 添加日志序号
  }
  
  // 设置多种数据格式以提高兼容性
  event.dataTransfer.setData('application/json', JSON.stringify(dragData))
  event.dataTransfer.setData('text/plain', rawContent)
  event.dataTransfer.setData('text/html', `<div>${rawContent}</div>`)
  
  // 设置拖拽效果
  event.dataTransfer.effectAllowed = 'copy'
  
  // 在Tauri环境中，确保拖拽功能启用
  if (event.dataTransfer.setDragImage) {
    // 创建一个透明的拖拽图像
    const dragImage = new Image()
    dragImage.src = ''
    event.dataTransfer.setDragImage(dragImage, 0, 0)
  }
}

  // 安全更新框架日志虚拟列表，避免拖拽时重新渲染
  const safeUpdateFrameworkVirtualList = () => {
    if (!isResizing.value) {
      // 先保存当前滚动位置
      const frameworkContainer = document.querySelector('.framework-logs') as HTMLElement
      if (frameworkContainer) {
        scrollPositionMemory.value.set('framework', frameworkContainer.scrollTop)
      }
      
      frameworkVirtualListKey.value++
      nextTick(() => {
        // 使用多次尝试恢复滚动位置，确保虚拟列表完全渲染后恢复
        const restoreScrollPosition = () => {
          const container = document.querySelector('.framework-logs') as HTMLElement
          if (container) {
            const savedPosition = scrollPositionMemory.value.get('framework')
            if (savedPosition !== undefined) {
              // 确保滚动位置在有效范围内
              const maxScrollTop = container.scrollHeight - container.clientHeight
              const targetPosition = Math.min(savedPosition, Math.max(0, maxScrollTop))
              if (container.scrollTop !== targetPosition) {
                container.scrollTop = targetPosition
              }
            }
          }
        }
        
        // 立即尝试恢复
        restoreScrollPosition()
        
        // 延迟再次尝试恢复，确保虚拟列表完全渲染
        setTimeout(restoreScrollPosition, 10)
        setTimeout(restoreScrollPosition, 50)
        setTimeout(restoreScrollPosition, 100)
      })
    }
  }

  // 安全更新操作日志虚拟列表，避免拖拽时重新渲染
  const safeUpdateOperationVirtualList = () => {
    if (!isResizing.value) {
      // 先保存当前滚动位置
      const operationContainer = document.querySelector('.operation-logs') as HTMLElement
      if (operationContainer) {
        scrollPositionMemory.value.set('operation', operationContainer.scrollTop)
      }
      
      operationVirtualListKey.value++
      nextTick(() => {
        // 使用多次尝试恢复滚动位置，确保虚拟列表完全渲染后恢复
        const restoreScrollPosition = () => {
          const container = document.querySelector('.operation-logs') as HTMLElement
          if (container) {
            const savedPosition = scrollPositionMemory.value.get('operation') || 0
            if (container.scrollTop !== savedPosition) {
              container.scrollTop = savedPosition
            }
          }
        }
        
        // 立即尝试恢复
        restoreScrollPosition()
        
        // 延迟再次尝试恢复，确保虚拟列表完全渲染
        setTimeout(restoreScrollPosition, 10)
        setTimeout(restoreScrollPosition, 50)
        setTimeout(restoreScrollPosition, 100)
      })
    }
  }
</script>

<style scoped>
.log-viewer {
  width: 100%;
  min-height: 100vh;
  padding: 8px 0;
  box-sizing: border-box;
  background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
  position: relative;
  height: auto;
  max-width: none;
  margin: 0;
}

.log-viewer::before {
  content: '';
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: 
    radial-gradient(circle at 20% 80%, rgba(59, 130, 246, 0.03) 0%, transparent 50%),
    radial-gradient(circle at 80% 20%, rgba(139, 92, 246, 0.03) 0%, transparent 50%);
  pointer-events: none;
  z-index: -1;
}

.main-layout {
  display: flex;
  gap: 16px;
  width: 100%;
  min-height: calc(100vh - 24px);
  position: relative;
}

.main-layout::before {
  content: '';
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 1px;
  height: 100%;
  background: linear-gradient(180deg, transparent, rgba(0, 0, 0, 0.04), transparent);
  opacity: 0.5;
}

.left-panel {
  min-width: 180px;
  width: max-content;
  max-width: 350px;
  flex-shrink: 0;
  position: relative;
  height: auto;
  margin-left: 8px;
}

.left-panel::before {
  content: '';
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 1px;
  background: linear-gradient(180deg, transparent, rgba(0, 0, 0, 0.06), transparent);
}

.right-panel {
  flex: 1;
  min-width: 0;
  position: relative;
  left:30px;
}

.right-panel::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(0, 0, 0, 0.06), transparent);
}

.filter-card {
  padding: 8px 12px;
  margin-bottom: 8px;
  border-radius: 10px;
  box-shadow: 0 3px 16px rgba(0, 0, 0, 0.06);
  border: 1px solid rgba(0, 0, 0, 0.04);
  background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
  transition: all 0.3s ease;
  position: relative;
  overflow: hidden;
}

.filter-card::after {
  content: '';
  position: absolute;
  top: 0;
  left: -100%;
  width: 100%;
  height: 100%;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.6), transparent);
  transition: left 0.8s ease;
  pointer-events: none;
}

.filter-card:hover::after {
  left: 100%;
}

.filter-card::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 4px;
  background: linear-gradient(90deg, #3b82f6 0%, #8b5cf6 50%, #3b82f6 100%);
  border-radius: 12px 12px 0 0;
  box-shadow: 0 2px 8px rgba(59, 130, 246, 0.3);
}

.filter-card .n-card-header {
  background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
  margin: -10px -14px 10px -14px;
  padding: 10px 14px;
  border-bottom: 1px solid rgba(0, 0, 0, 0.04);
  border-radius: 12px 12px 0 0;
}

.filter-card .n-card-header .n-card-header__main {
  font-weight: 600;
  color: #1e293b;
  font-size: 15px;
  display: flex;
  align-items: center;
  gap: 10px;
  letter-spacing: 0.5px;
}

.filter-card .n-card-header .n-card-header__main::before {
  content: '';
  width: 4px;
  height: 18px;
  background: linear-gradient(135deg, #3b82f6, #8b5cf6);
  border-radius: 2px;
  box-shadow: 0 2px 6px rgba(59, 130, 246, 0.3);
}

.filter-card:hover {
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.12), 0 0 0 1px rgba(59, 130, 246, 0.15);
  transform: translateY(-2px);
  border-color: rgba(59, 130, 246, 0.25);
}

.log-type-card {
  padding: 16px;
  margin-bottom: 20px;
  width: max-content;
  min-width: 220px;
  border-radius: 12px;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
  border: 1px solid rgba(0, 0, 0, 0.06);
  background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
  transition: all 0.3s ease;
  position: relative;
  height: auto;
}

.log-type-card::after {
  content: '';
  position: absolute;
  top: 0;
  left: -100%;
  width: 100%;
  height: 100%;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.6), transparent);
  transition: left 0.8s ease;
  pointer-events: none;
}

.log-type-card:hover::after {
  left: 100%;
}

.log-type-card::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 4px;
  background: linear-gradient(90deg, #3b82f6 0%, #8b5cf6 50%, #3b82f6 100%);
  border-radius: 12px 12px 0 0;
  box-shadow: 0 2px 8px rgba(59, 130, 246, 0.3);
}

.log-type-card:hover {
  box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
  transform: translateY(-2px);
}

.filter-row {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  padding: 4px 0;
  margin: 0 -4px;
  min-width: 0;
}

@media (max-width: 768px) {
  .filter-row {
    gap: 16px;
  }
  
  .filter-item {
    padding: 6px 10px;
  }
  
  .log-type-card {
    min-width: 200px;
    max-width: 280px;
    padding: 10px 12px;
  }
  
  .header-icon {
    padding: 4px 8px;
    font-size: 11px;
  }
  
  .card-header-icons {
    gap: 6px;
    margin-left: 8px;
    padding: 3px;
  }
}

.filter-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 8px;
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.9), rgba(248, 250, 252, 0.8));
  border-radius: 10px;
  border: 1px solid rgba(0, 0, 0, 0.04);
  transition: all 0.25s ease;
  position: relative;
  backdrop-filter: blur(10px);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.02);
}

.filter-item:hover {
  background: linear-gradient(135deg, rgba(255, 255, 255, 1), rgba(239, 246, 255, 0.95));
  border-color: rgba(59, 130, 246, 0.2);
  transform: translateY(-2px);
  box-shadow: 0 4px 16px rgba(59, 130, 246, 0.15), 0 2px 8px rgba(0, 0, 0, 0.08);
}

.filter-item::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 4px;
  background: linear-gradient(135deg, #3b82f6, #8b5cf6);
  border-radius: 10px 0 0 10px;
  opacity: 0;
  transition: all 0.25s ease;
  box-shadow: 0 0 8px rgba(59, 130, 246, 0.4);
}

.filter-item:hover::before {
  opacity: 1;
  width: 5px;
}

/* 筛选框内输入控件的样式优化 */
.filter-item .n-input,
.filter-item .n-date-picker {
  transition: all 0.2s ease;
}

.filter-item .n-input:hover,
.filter-item .n-date-picker:hover {
  transform: translateY(-1px);
}

.filter-item .n-input .n-input__input-el,
.filter-item .n-date-picker .n-input__input-el {
  border-radius: 8px;
  border: 1px solid rgba(0, 0, 0, 0.06);
  transition: all 0.25s ease;
  background: rgba(255, 255, 255, 0.8);
  font-size: 13px;
}

.filter-item .n-input .n-input__input-el:focus,
.filter-item .n-date-picker .n-input__input-el:focus {
  border-color: #3b82f6;
  box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15), 0 2px 8px rgba(59, 130, 246, 0.1);
  background: rgba(255, 255, 255, 1);
  transform: scale(1.02);
}

.filter-item .n-input .n-input__prefix {
  color: #64748b;
  transition: all 0.25s ease;
  font-size: 16px;
}

.filter-item .n-input:hover .n-input__prefix {
  color: #3b82f6;
  transform: scale(1.1);
}

.log-type-switches {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0;
  margin-bottom: 0;
  width: 100%;
}



.log-type-section {
  width: 100%;
  margin-bottom: 4px;
}

.log-type-section:last-child {
  margin-bottom: 0;
}

.debug-log-section {
  width: 100%;
  margin-top: 4px;
  padding-top: 4px;
  border-top: 1px solid rgba(0, 0, 0, 0.06);
}

.log-type-title {
  font-size: 16px;
  font-weight: 600;
  margin-bottom: 12px;
  color: #374151;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  position: relative;
  padding-left: 20px;
}

.log-type-title::before {
  content: '';
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 4px;
  height: 16px;
  background: linear-gradient(135deg, #3b82f6, #8b5cf6);
  border-radius: 2px;
}

.debug-log-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 12px;
}

.debug-log-title {
  font-size: 16px;
  font-weight: 600;
  color: #374151;
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 0;
}

.debug-log-title::before {
  content: '';
  position: relative;
  width: 4px;
  height: 16px;
  background: linear-gradient(135deg, #3b82f6, #8b5cf6);
  border-radius: 2px;
  margin-right: 8px;
}

.debug-log-search {
  flex-shrink: 0;
  margin-left: 16px;
}

.debug-log-search .n-input {
  transition: all 0.25s ease;
  width: 220px;
}

.debug-log-search .n-input:hover {
  transform: translateY(-2px);
}

.debug-log-search .n-input .n-input__input-el {
  border-radius: 8px;
  border: 1px solid rgba(0, 0, 0, 0.06);
  transition: all 0.25s ease;
  background: rgba(255, 255, 255, 0.8);
  font-size: 14px;
  height: 36px;
  line-height: 36px;
}

.debug-log-search .n-input .n-input__input-el:focus {
  border-color: #3b82f6;
  box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.2), 0 4px 12px rgba(59, 130, 246, 0.15);
  background: rgba(255, 255, 255, 1);
  transform: scale(1.03);
}

.debug-log-search .n-input .n-input__prefix {
  color: #64748b;
  transition: all 0.25s ease;
  font-size: 18px;
}

.debug-log-search .n-input:hover .n-input__prefix {
  color: #3b82f6;
  transform: scale(1.1);
}

.component-list {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-height: 100px;
  position: relative;
}

.component-checkbox-item {
  display: flex;
  align-items: center;
  white-space: nowrap;
  min-width: max-content;
  padding: 4px 8px;
  background: rgba(255, 255, 255, 0.6);
  border-radius: 5px;
  border: 1px solid rgba(0, 0, 0, 0.03);
  transition: all 0.2s ease;
}

.component-checkbox-item:hover {
  background: linear-gradient(135deg, rgba(255, 255, 255, 1), rgba(239, 246, 255, 0.9));
  border-color: rgba(59, 130, 246, 0.2);
  transform: translateX(3px);
  box-shadow: 0 4px 16px rgba(59, 130, 246, 0.15);
}

/* 组件项包装器样式 */
.component-item-wrapper {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 12px;
  border-radius: 8px;
  border: 1px solid rgba(59, 130, 246, 0.15);
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.8) 0%, rgba(248, 250, 252, 0.9) 100%);
  box-shadow: 0 2px 8px rgba(59, 130, 246, 0.08), 0 1px 3px rgba(0, 0, 0, 0.05);
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  position: relative;
  overflow: hidden;
}

.component-item-wrapper::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 2px;
  background: linear-gradient(90deg, rgba(59, 130, 246, 0.3) 0%, rgba(147, 197, 253, 0.3) 50%, rgba(59, 130, 246, 0.3) 100%);
  opacity: 0;
  transition: opacity 0.3s ease;
}

.component-item-wrapper:hover {
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.95) 0%, rgba(241, 245, 249, 0.95) 100%);
  border-color: rgba(59, 130, 246, 0.25);
  box-shadow: 0 4px 16px rgba(59, 130, 246, 0.12), 0 2px 8px rgba(0, 0, 0, 0.08);
  transform: translateY(-1px);
}

.component-item-wrapper:hover::before {
  opacity: 1;
}

.component-item-wrapper:active {
  transform: translateY(0);
  box-shadow: 0 2px 8px rgba(59, 130, 246, 0.08), 0 1px 3px rgba(0, 0, 0, 0.05);
}

/* 组件无日志时的置灰样式 */
.component-item-wrapper.component-no-logs {
  border-color: rgba(156, 163, 175, 0.2);
  background: linear-gradient(135deg, rgba(249, 250, 251, 0.8) 0%, rgba(243, 244, 246, 0.9) 100%);
  box-shadow: 0 1px 4px rgba(156, 163, 175, 0.1);
}

.component-item-wrapper.component-no-logs .n-checkbox__label {
  color: #9ca3af !important;
}

.component-item-wrapper.component-no-logs .n-checkbox {
  opacity: 0.6;
}

/* MDB按钮不受置灰影响 */
.component-item-wrapper.component-no-logs .mdb-btn {
  opacity: 1 !important;
  filter: none !important;
}

.component-item-wrapper.component-no-logs .mdb-btn .mdb-text {
  opacity: 1 !important;
  color: #3b82f6 !important;
}

/* 复选框组件样式优化 */
:deep(.component-checkbox-item .n-checkbox) {
  transition: all 0.25s ease;
}

/* 页面加载动画 */
@keyframes fadeInUp {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes slideInLeft {
  from {
    opacity: 0;
    transform: translateX(-20px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

@keyframes slideInRight {
  from {
    opacity: 0;
    transform: translateX(20px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

/* 应用动画到各个组件 */
.log-type-card {
  animation: slideInLeft 0.6s ease-out;
}

.filter-card {
  animation: slideInRight 0.6s ease-out 0.1s both;
}

.log-content-area {
  animation: fadeInUp 0.6s ease-out 0.2s both;
}

/* 滚动条美化 */
:deep(::-webkit-scrollbar) {
  width: 8px;
  height: 8px;
}

:deep(::-webkit-scrollbar-track) {
  background: rgba(0, 0, 0, 0.02);
  border-radius: 4px;
}

:deep(::-webkit-scrollbar-thumb) {
  background: linear-gradient(135deg, rgba(59, 130, 246, 0.3), rgba(139, 92, 246, 0.3));
  border-radius: 4px;
  transition: all 0.25s ease;
}

:deep(::-webkit-scrollbar-thumb:hover) {
  background: linear-gradient(135deg, rgba(59, 130, 246, 0.5), rgba(139, 92, 246, 0.5));
}

/* 焦点状态美化 */
:deep(*:focus) {
  outline: none;
  box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}

/* 选择文本美化 */
:deep(::selection) {
  background: rgba(59, 130, 246, 0.2);
  color: #1f2937;
}

:deep(.component-checkbox-item .n-checkbox:hover) {
  transform: scale(1.02);
}

:deep(.component-checkbox-item .n-checkbox .n-checkbox__input) {
  border: 2px solid rgba(0, 0, 0, 0.1);
  border-radius: 4px;
  transition: all 0.25s ease;
}

:deep(.component-checkbox-item .n-checkbox--checked .n-checkbox__input) {
  border-color: #3b82f6;
  background: linear-gradient(135deg, #3b82f6, #8b5cf6);
  box-shadow: 0 2px 8px rgba(59, 130, 246, 0.3);
}

:deep(.component-checkbox-item .n-checkbox__label) {
  color: #374151;
  font-weight: 500;
  transition: color 0.25s ease;
}

:deep(.component-checkbox-item:hover .n-checkbox__label) {
  color: #1f2937;
}

.no-components {
  color: #9ca3af;
  font-size: 12px;
  text-align: center;
  padding: 16px;
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.6), rgba(248, 250, 252, 0.4));
  border-radius: 8px;
  border: 1px dashed rgba(0, 0, 0, 0.1);
  backdrop-filter: blur(8px);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.02);
  transition: all 0.25s ease;
  margin: 8px 0;
  min-height: 80px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.no-components:hover {
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.8), rgba(248, 250, 252, 0.6));
  border-color: rgba(59, 130, 246, 0.2);
  box-shadow: 0 2px 8px rgba(59, 130, 246, 0.1);
}

.no-components-text {
  font-size: 14px;
  font-weight: 500;
  margin-bottom: 8px;
}

.debug-info-panel {
  margin-top: 12px;
  padding: 12px;
  background: rgba(245, 245, 245, 0.8);
  border-radius: 6px;
  font-size: 12px;
  max-height: 200px;
  overflow: auto;
  border: 1px solid rgba(0, 0, 0, 0.06);
  width: 100%;
  max-width: 300px;
}

.search-tip {
  font-size: 11px;
  color: #6b7280;
  margin-bottom: 8px;
  padding: 4px 8px;
  background: rgba(59, 130, 246, 0.1);
  border-radius: 4px;
  border: 1px solid rgba(59, 130, 246, 0.2);
}

/* BMC版本和组件版本与操作日志之间的分割线 */
.version-separator {
  width: 100%;
  height: 2px;
  background: linear-gradient(90deg, #e3f2fd 0%, #64b5f6 50%, #e3f2fd 100%);
  margin: 2px 0 6px 0;
  border-radius: 1px;
  box-shadow: 0 2px 8px rgba(100, 181, 246, 0.4);
  position: relative;
  overflow: hidden;
}

.version-separator::after {
  content: '';
  position: absolute;
  top: 0;
  left: -100%;
  width: 100%;
  height: 100%;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.6), transparent);
  transition: left 1s ease;
  pointer-events: none;
}

.version-separator:hover::after {
  left: 100%;
}

/* 操作日志、告警日志、框架日志之间的分割线 */
.log-type-separator {
  width: 100%;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(0, 0, 0, 0.08), transparent);
  margin: 2px 0 6px 0;
  position: relative;
  overflow: hidden;
}

.log-type-separator::after {
  content: '';
  position: absolute;
  top: 0;
  left: -100%;
  width: 100%;
  height: 100%;
  background: linear-gradient(90deg, transparent, rgba(59, 130, 246, 0.3), transparent);
  transition: left 0.8s ease;
  pointer-events: none;
}

.log-type-separator:hover::after {
  left: 100%;
}

/* 卡片标题图标样式 */
.card-header-icons {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-left: 12px;
  padding: 4px;
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.8), rgba(248, 250, 252, 0.6));
  border-radius: 10px;
  border: 1px solid rgba(0, 0, 0, 0.04);
  backdrop-filter: blur(10px);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.02);
}

.header-icon {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  border-radius: 8px;
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  transition: all 0.25s ease;
  white-space: nowrap;
  z-index: 10;
  position: relative;
  backdrop-filter: blur(10px);
  cursor: pointer;
  user-select: none;
}

.bmc-icon {
  background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%);
  color: #1976d2;
  box-shadow: 0 2px 8px rgba(25, 118, 210, 0.2);
  border: 1px solid rgba(25, 118, 210, 0.25);
  position: relative;
  overflow: hidden;
}

.bmc-icon::before {
  content: '';
  position: absolute;
  top: 0;
  left: -100%;
  width: 100%;
  height: 100%;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent);
  transition: left 0.6s ease;
  pointer-events: none;
}

.bmc-icon:hover {
  background: linear-gradient(135deg, #bbdefb 0%, #90caf9 100%);
  box-shadow: 0 4px 16px rgba(25, 118, 210, 0.3);
  transform: translateY(-2px);
  border-color: rgba(25, 118, 210, 0.4);
}

.bmc-icon:active {
  transform: translateY(0);
  box-shadow: 0 2px 8px rgba(25, 118, 210, 0.2);
}

.bmc-icon:hover::before {
  left: 100%;
}

.app-icon {
  background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%);
  color: #1976d2;
  box-shadow: 0 2px 8px rgba(25, 118, 210, 0.2);
  border: 1px solid rgba(25, 118, 210, 0.25);
  position: relative;
  overflow: hidden;
}

.app-icon::before {
  content: '';
  position: absolute;
  top: 0;
  left: -100%;
  width: 100%;
  height: 100%;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent);
  transition: left 0.6s ease;
  pointer-events: none;
}

.app-icon:hover {
  background: linear-gradient(135deg, #bbdefb 0%, #90caf9 100%);
  box-shadow: 0 4px 16px rgba(25, 118, 210, 0.3);
  transform: translateY(-2px);
  border-color: rgba(25, 118, 210, 0.4);
}

.app-icon:active {
  transform: translateY(0);
  box-shadow: 0 2px 8px rgba(25, 118, 210, 0.2);
}

.app-icon:hover::before {
  left: 100%;
}

.switch-item .n-switch {
  min-width: 80px;
  height: 32px;
}

/* 开关组件样式优化 - 使用更强的选择器 */
.log-viewer .log-type-title .n-switch {
  transition: all 0.25s ease;
  margin-left: -4px !important;
}

/* 最强优先级 - 确保开关位置生效 */
#app .log-viewer .log-type-title .n-switch {
  margin-left: -4px !important;
}

/* 特定开关类样式 - 确保位置生效 */
.operation-switch,
.alert-switch,
.framework-switch {
  margin-left: -4px !important;
  transform: translateX(-4px) !important;
  position: relative !important;
  left: -4px !important;
}

.log-viewer .log-type-title .n-switch:hover {
  transform: scale(1.05);
}

.log-viewer .log-type-title .n-switch .n-switch__rail {
  background: #e2e8f0 !important;
  border: 1px solid #d1d5db !important;
  transition: all 0.25s ease;
}

.log-viewer .log-type-title .n-switch--checked .n-switch__rail {
  background: #10b981 !important;
  border-color: #059669 !important;
  box-shadow: 0 2px 8px rgba(16, 185, 129, 0.3);
}

.log-viewer .log-type-title .n-switch__button {
  background: #ffffff !important;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  transition: all 0.25s ease;
}

.log-viewer .log-type-title .n-switch--checked .n-switch__button {
  background: #ffffff !important;
  transform: translateX(16px);
}

.log-viewer .log-type-title .n-switch:hover .n-switch__button {
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}

/* 额外的开关样式覆盖 - 确保优先级 */
.log-viewer .log-type-title .n-switch .n-switch__rail {
  background: #e2e8f0 !important;
  border: 1px solid #d1d5db !important;
}

.log-viewer .log-type-title .n-switch--checked .n-switch__rail {
  background: #10b981 !important;
  border-color: #059669 !important;
}

.log-viewer .log-type-title .n-switch__button {
  background: #ffffff !important;
}

.log-viewer .log-type-title .n-switch--checked .n-switch__button {
  background: #ffffff !important;
}

/* 最强优先级覆盖 - 确保开关颜色正确显示 */
.log-viewer .log-type-title .n-switch .n-switch__rail {
  background: #e2e8f0 !important;
  border: 1px solid #d1d5db !important;
}

.log-viewer .log-type-title .n-switch--checked .n-switch__rail {
  background: #10b981 !important;
  border-color: #059669 !important;
}

.log-viewer .log-type-title .n-switch__button {
  background: #ffffff !important;
}

.log-viewer .log-type-title .n-switch--checked .n-switch__button {
  background: #ffffff !important;
}

/* 全局开关样式重置 - 防止其他样式干扰 */
.n-switch .n-switch__rail {
  background: #e2e8f0 !important;
  border: 1px solid #d1d5db !important;
}

.n-switch--checked .n-switch__rail {
  background: #10b981 !important;
  border-color: #059669 !important;
}

.n-switch__button {
  background: #ffffff !important;
}

.n-switch--checked .n-switch__button {
  background: #ffffff !important;
}

/* 最强优先级 - 使用属性选择器 */
[class*="n-switch"] .n-switch__rail {
  background: #e2e8f0 !important;
  border: 1px solid #d1d5db !important;
}

[class*="n-switch--checked"] .n-switch__rail {
  background: #10b981 !important;
  border-color: #059669 !important;
}

[class*="n-switch"] .n-switch__button {
  background: #ffffff !important;
}

[class*="n-switch--checked"] .n-switch__button {
  background: #ffffff !important;
}

/* 使用ID选择器确保最高优先级 */
#app .log-viewer .log-type-title .n-switch .n-switch__rail {
  background: #e2e8f0 !important;
  border: 1px solid #d1d5db !important;
}

#app .log-viewer .log-type-title .n-switch--checked .n-switch__rail {
  background: #10b981 !important;
  border-color: #059669 !important;
}

#app .log-viewer .log-type-title .n-switch__button {
  background: #ffffff !important;
}

#app .log-viewer .log-type-title .n-switch--checked .n-switch__button {
  background: #ffffff !important;
}

/* 使用CSS变量覆盖 - 最高优先级 */
:root {
  --n-switch-rail-color: #e2e8f0 !important;
  --n-switch-rail-color-checked: #10b981 !important;
  --n-switch-button-color: #ffffff !important;
  --n-switch-button-color-checked: #ffffff !important;
}

/* 强制覆盖所有开关样式 */
* .n-switch .n-switch__rail {
  background: var(--n-switch-rail-color) !important;
  border: 1px solid #d1d5db !important;
}

* .n-switch--checked .n-switch__rail {
  background: var(--n-switch-rail-color-checked) !important;
  border-color: #059669 !important;
}

* .n-switch__button {
  background: var(--n-switch-button-color) !important;
}

* .n-switch--checked .n-switch__button {
  background: var(--n-switch-button-color-checked) !important;
}



/* 最终强制覆盖 - 使用所有可能的选择器 */
.log-viewer .log-type-title .n-switch .n-switch__rail,
.log-viewer .log-type-title .n-switch .n-switch__rail,
.log-viewer .log-type-title .n-switch .n-switch__rail {
  background: #e2e8f0 !important;
  border: 1px solid #d1d5db !important;
}

.log-viewer .log-type-title .n-switch--checked .n-switch__rail,
.log-viewer .log-type-title .n-switch--checked .n-switch__rail,
.log-viewer .log-type-title .n-switch--checked .n-switch__rail {
  background: #10b981 !important;
  border-color: #059669 !important;
}

.switch-item .n-select {
  min-width: 200px;
  flex: 1;
  max-width: none;
  height: 32px;
}

.log-content-area {
  display: flex;
  flex-direction: column;
  gap: 16px;
  will-change: height;
  transform: translateZ(0);
}

.component-content {
  display: flex;
  gap: 16px;
  height: 100%;
  min-height: 200px;
  will-change: height;
  transform: translateZ(0);
}

.stats-panel {
  width: 300px;
  flex-shrink: 0;
}

.stats-card {
  height: 100%;
  position: relative;
  padding-bottom: 50px; /* 为底部按钮留出空间 */
}

.logs-panel {
  flex: 1;
  min-width: 0;
  height: 100%;
  will-change: height;
  transform: translateZ(0);
}

.logs-card {
  height: 100%;
  display: flex;
  flex-direction: column;
  padding: 0;
}

.log-content {
  height: 100%;
  border: 1px solid #f0f0f0;
  border-radius: 6px;
}

.log-item {
  padding: 8px 12px;
  border-bottom: 1px solid var(--n-border-color);
  cursor: pointer;
  transition: background-color 0.2s ease;
  user-select: none;
  background-color: #ffffff;
}

.log-item-even {
  background-color: #f8f9fa !important;
}

.log-item:hover {
  background-color: var(--n-hover-color) !important;
}

.log-item:hover {
  background-color: var(--n-hover-color);
}

.log-item:active {
  background-color: var(--n-active-color);
}

.log-line {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 4px;
  width: 100%;
  flex-wrap: wrap;
}

.log-line-number {
  min-width: 50px;
  flex-shrink: 0;
  font-weight: 600;
  color: #666;
  text-align: center;
  font-size: 12px;
}

.log-time {
  min-width: 150px;
  flex-shrink: 0;
}

.log-time.selectable-text {
  cursor: text;
  user-select: text;
}

.log-time.selectable-text:hover {
  background-color: var(--n-color-hover);
  border-radius: 4px;
  padding: 2px 4px;
}

.log-component {
  min-width: 120px;
  flex-shrink: 0;
}

.log-level {
  min-width: 60px;
  flex-shrink: 0;
}

/* 日志级别颜色样式 */
.log-level-error {
  color: #ff4d4f;
  font-weight: 600;
}

.log-level-warn {
  color: #faad14;
  font-weight: 600;
}

.log-level-info {
  color: #1890ff;
  font-weight: 600;
}

.log-level-debug {
  color: #52c41a;
  font-weight: 600;
}

.log-level-skynet {
  color: #722ed1;
  font-weight: 600;
}

.log-level-unknown {
  color: #8c8c8c;
  font-weight: 600;
}

/* Interface颜色样式 */
.log-interface {
  font-weight: 600;
}

.log-interface-redfish {
  color: #1890ff;
}

.log-interface-web {
  color: #52c41a;
}

.log-interface-ipmi {
  color: #fa8c16;
}

.log-interface-cli {
  color: #722ed1;
}

.log-interface-busctl {
  color: #13c2c2;
}

.log-interface-mdbctl {
  color: #eb2f96;
}

.log-interface-unknown {
  color: #8c8c8c;
}

.log-message.selectable-text {
  cursor: text;
  user-select: text;
  flex: 1;
  min-width: 0;
  word-wrap: break-word;
  word-break: break-all;
  white-space: normal;
  line-height: 1.4;
}

.log-message.selectable-text:hover {
  background-color: var(--n-color-hover);
  border-radius: 4px;
  padding: 2px 4px;
}

.log-actions {
  display: flex;
  align-items: center;
  gap: 4px;
  opacity: 0;
  transition: opacity 0.2s ease;
}

.log-item:hover .log-actions {
  opacity: 1;
}

.copy-btn {
  width: 20px;
  height: 20px;
  min-width: 20px;
  padding: 0;
}

.copy-btn .n-icon {
  font-size: 12px;
}

.delete-btn {
  width: 32px;
  height: 32px;
  min-width: 32px;
  padding: 0;
  color: #ef4444;
  flex-shrink: 0;
}

.delete-btn:hover {
  color: #dc2626;
  background-color: rgba(239, 68, 68, 0.1);
}

.delete-btn .n-icon {
  font-size: 18px;
}

.operation-search-input {
  width: 33.33% !important;
  min-width: 200px;
  max-width: 300px;
}

.interface-select {
  width: 200px;
  flex-shrink: 0;
  margin-left: 8px;
}

.framework-search-input {
  width: 300px !important;
  min-width: 300px;
  max-width: 300px;
  flex-shrink: 0;
}

.alert-search-input {
  width: 300px !important;
  min-width: 300px;
  max-width: 300px;
  flex-shrink: 0;
}


/* 确保Interface下拉框只显示第一个标签，其他用数字表示 */
.interface-select :deep(.n-base-selection) {
  .n-base-selection-tag-wrapper {
    max-width: 180px;
    overflow: hidden;
  }
  
  .n-base-selection-tag {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
  }
  
  /* 隐藏除第一个之外的所有标签 */
  .n-base-selection-tag:not(:first-child) {
    display: none;
  }
  
  /* 第一个标签的样式 */
  .n-base-selection-tag:first-child {
    max-width: 180px;
  }
  
  /* 添加计数器显示其他选中的数量 */
  .n-base-selection-tag:first-child::after {
    content: attr(data-extra-count);
    display: inline-block;
    margin-left: 4px;
    padding: 2px 6px;
    background-color: #f0f0f0;
    border-radius: 10px;
    font-size: 11px;
    color: #666;
    font-weight: 500;
  }
}

/* 框架日志级别开关样式 */
.level-filters {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-shrink: 0;
  min-width: 200px;
  max-width: 400px;
}

.level-filter-item {
  display: flex;
  align-items: center;
  padding: 2px;
}

/* 框架日志级别开关字体样式 */
.level-filter-item :deep(.n-switch) {
  font-size: 10px !important;
  font-weight: 400 !important;
}

.level-filter-item :deep(.n-switch .n-switch__rail) {
  height: 18px !important;
  min-width: 32px !important;
}

.level-filter-item :deep(.n-switch .n-switch__button) {
  width: 14px !important;
  height: 14px !important;
}

/* 确保框架日志级别开关字体更小 */
.framework-stats-panel .level-filter-item :deep(.n-switch) {
  font-size: 10px !important;
  font-weight: 400 !important;
}

/* 自定义Interface标签样式 */
.custom-interface-tag {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 8px;
  background-color: #e6f4ff;
  border: 1px solid #91d5ff;
  border-radius: 6px;
  font-size: 12px;
  color: #1890ff;
  max-width: 180px;
  overflow: hidden;
}

.tag-text {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex: 1;
}

.extra-count {
  background-color: #f0f0f0;
  color: #666;
  padding: 2px 6px;
  border-radius: 10px;
  font-size: 11px;
  font-weight: 500;
  flex-shrink: 0;
}

.tag-close-btn {
  background: none;
  border: none;
  color: #999;
  cursor: pointer;
  font-size: 14px;
  padding: 0;
  width: 16px;
  height: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  flex-shrink: 0;
}

.tag-close-btn:hover {
  background-color: #f0f0f0;
  color: #666;
}

.empty-logs {
  padding: 40px;
  text-align: center;
}

/* 标题栏右侧样式 */
.header-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* 底部操作按钮样式 */
.stats-actions {
  display: flex;
  justify-content: center;
  gap: 8px;
  padding: 8px 0;
  border-top: 1px solid #f0f0f0;
  margin-top: 8px;
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  background-color: #fff;
}

.mdb-btn {
  color: #1890ff;
  transition: all 0.2s ease;
  border: 1px solid #1890ff;
  width: 24px;
  height: 24px;
  min-width: 24px;
  min-height: 24px;
  background-color: #f0f8ff;
  border-radius: 50%;
  box-shadow: none;
}

.mdb-btn:hover {
  color: #40a9ff;
  border-color: #40a9ff;
  background-color: #e6f7ff;
  box-shadow: 0 2px 8px rgba(24, 144, 255, 0.3);
  transform: translateY(-1px);
}

       /* CSR按钮专用样式 - 蓝色、更突出 */
       .csr-btn {
         background-color: #60a5fa !important;
         border-color: #3b82f6 !important;
         color: #ffffff !important;
         font-weight: 600;
         box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
         width: 32px;
         height: 32px;
         min-width: 32px;
         min-height: 32px;
       }

       .csr-btn:hover {
         background-color: #3b82f6 !important;
         border-color: #2563eb !important;
         color: #ffffff !important;
         transform: scale(1.05);
         box-shadow: 0 4px 8px rgba(0, 0, 0, 0.4);
       }

       /* CSR和BMC按钮之间的分割线 */
       .csr-bmc-separator {
         width: 1px;
         height: 20px;
         background-color: #e2e8f0;
         margin: 0 8px;
         border-radius: 1px;
       }

       /* CSR内容样式 */
       .csr-content {
         padding: 16px;
         background-color: #fafafa;
         border-radius: 6px;
         /* 添加滚动功能，让内容可以滚动而搜索框保持固定 */
         max-height: calc(100vh - 200px);
         overflow-y: auto;
       }

       .csr-text-content {
         font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
         font-size: 13px;
         line-height: 1.6;
         color: #2d3748;
         white-space: pre-wrap;
         word-wrap: break-word;
       }

       .csr-text-line {
         position: relative;
         padding: 2px 0;
         transition: background-color 0.2s ease;
       }

       .csr-text-line:hover {
         background-color: rgba(59, 130, 246, 0.05);
         border-radius: 4px;
       }

       /* 连接器Presence状态颜色样式 */
       .csr-text-line.connector-presence-active {
         background-color: rgba(240, 248, 255, 0.8); /* 非常淡的蓝色背景 */
         border-left: 3px solid #4A90E2; /* 左侧蓝色边框 */
         border-radius: 4px;
         padding-left: 8px;
       }

       .csr-text-line.connector-presence-inactive {
         background-color: rgba(248, 248, 248, 0.6); /* 淡灰色背景 */
         border-left: 3px solid #D3D3D3; /* 左侧灰色边框 */
         border-radius: 4px;
         padding-left: 8px;
       }

       .csr-text-line.connector-presence-active:hover {
         background-color: rgba(230, 244, 255, 0.9);
         border-left-color: #357ABD;
       }

       .csr-text-line.connector-presence-inactive:hover {
         background-color: rgba(240, 240, 240, 0.8);
         border-left-color: #B0B0B0;
       }

       .csr-text {
         font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
         font-size: 13px;
         color: #2d3748;
       }

       .connector-detail-btn {
         position: absolute;
         right: 8px;
         top: 50%;
         transform: translateY(-50%);
         margin-left: 12px;
         font-size: 11px;
         height: 24px;
         padding: 0 8px;
         border-radius: 12px;
         opacity: 0;
         transition: opacity 0.2s ease;
         z-index: 10;
       }

       .csr-text-line:hover .connector-detail-btn {
         opacity: 1;
       }

       .connector-detail-btn.hover-only {
         display: inline-block;
       }

       /* Connector详细信息抽屉样式 */
       .connector-detail-content {
         height: 100%;
         display: flex;
         flex-direction: column;
         min-height: 600px;
       }

       .connector-detail-text {
         flex: 1;
         display: flex;
         flex-direction: column;
         background-color: #fafafa;
         border-radius: 6px;
       }

       /* Connector详细信息搜索容器样式 */
       .connector-detail-search-container {
         position: sticky;
         top: 0;
         z-index: 10;
         background: #fafafa;
         padding: 16px 16px 12px 16px;
         border-bottom: 1px solid #e8e8e8;
       }

       .connector-detail-search-bar {
         margin-bottom: 12px;
       }

       .connector-detail-search-nav {
         display: flex;
         align-items: center;
         justify-content: space-between;
         gap: 12px;
       }

       .search-info {
         font-size: 12px;
         color: #666;
       }

       .search-buttons {
         display: flex;
         gap: 8px;
       }

       /* Connector详细信息可滚动内容区域 */
       .connector-detail-scrollable-content {
         flex: 1;
         overflow-y: auto;
         padding: 16px;
         min-height: 400px;
       }

       .connector-detail-text pre {
         margin: 0;
         font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
         font-size: 12px;
         line-height: 1.5;
         color: #2d3748;
         white-space: pre-wrap;
         word-wrap: break-word;
       }

       /* 搜索高亮样式 */
       .search-highlight {
         background-color: #ffd54f;
         color: #000;
         padding: 1px 2px;
         border-radius: 2px;
         font-weight: bold;
       }

       /* 双色高亮样式 */
       .search-highlight-line {
         background-color: #fff3cd;
         border: 1px solid #ffc107;
         border-radius: 4px;
         padding: 2px 4px;
         margin: 1px 0;
       }

       .search-highlight-current {
         background-color: #ff9800;
         color: #fff;
         box-shadow: 0 0 0 2px #ff5722;
       }

       .search-highlight-current-line {
         background-color: #fff8e1;
         border: 2px solid #ff9800;
         box-shadow: 0 2px 8px rgba(255, 152, 0, 0.3);
       }

       .connector-detail-error {
         flex: 1;
         display: flex;
         align-items: center;
         justify-content: center;
       }

.mdb-text {
  font-size: 9px;
  font-weight: 600;
  font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.delete-btn {
  color: #ff4d4f;
  transition: all 0.2s ease;
  width: 24px;
  height: 24px;
  min-width: 24px;
  min-height: 24px;
  background-color: #fff2f0;
  border-radius: 50%;
  box-shadow: none;
}

.delete-btn:hover {
  color: #ff7875;
  background-color: #ffebee;
  box-shadow: 0 2px 8px rgba(255, 77, 79, 0.3);
  transform: translateY(-1px);
}

/* 组件搜索框样式 */
.component-search-bar {
  padding: 8px 12px;
  border-bottom: 1px solid #f0f0f0;
  background-color: #fafafa;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 40px;
}

.component-search-input {
  width: 300px !important;
  min-width: 300px;
  max-width: 300px;
}

.component-header {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}

.component-name {
  font-size: 14px;
  font-weight: 600;
  color: #374151;
  padding: 4px 8px;
  background: rgba(59, 130, 246, 0.1);
  border-radius: 6px;
  border: 1px solid rgba(59, 130, 246, 0.2);
  margin-right: 8px;
}

.level-filters {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-shrink: 0;
  min-width: 200px;
}

.hidden-stats {
  display: none;
}

.level-filter-item {
  display: flex;
  align-items: center;
  padding: 2px;
}



.logs-card {
  height: 100%;
  display: flex;
  flex-direction: column;
  padding: 0;
}

.log-content {
  height: calc(100% - 52px); /* 减去搜索框的高度 */
  border: 1px solid #f0f0f0;
  border-radius: 6px;
  flex: 1;
}

/* MDB 抽屉样式 */
.mdb-drawer-content {
  height: 100%;
  display: flex;
  flex-direction: column;
  position: relative;
}

/* 搜索容器样式 */
.mdb-search-container {
  position: sticky;
  top: 0;
  z-index: 10;
  background-color: white;
  padding: 16px;
  border-bottom: 1px solid #f0f0f0;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

/* 可滚动内容区域 */
.mdb-scrollable-content {
  flex: 1;
  overflow-y: auto;
  padding: 16px;
}

/* 确保抽屉内容容器有正确的定位上下文 */
:deep(.n-drawer-content) {
  position: relative;
}

.mdb-search-bar {
  margin-bottom: 8px;
}

/* 搜索导航样式 */
.mdb-search-nav {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px 16px;
  background-color: #f0f0f0;
  border-radius: 6px;
  margin-bottom: 16px;
}

.search-info {
  font-size: 12px;
  color: #666;
}

.search-controls {
  display: flex;
  gap: 8px;
}

.mdb-content {
  flex: 1;
  display: flex;
  flex-direction: column;
  background-color: #fafafa;
  border-radius: 6px;
  margin-bottom: 16px;
  /* 添加滚动功能 */
  overflow-y: auto;
  max-height: calc(100vh - 200px);
}

.mdb-content-text {
  flex: 1;
  overflow: auto;
  padding: 16px;
}

.mdb-content-text pre {
  margin: 0;
  font-family: 'Courier New', monospace;
  font-size: 12px;
  line-height: 1.5;
  color: #333;
  white-space: pre-wrap;
  word-wrap: break-word;
}

/* 搜索高亮样式 */
.search-highlight {
  background-color: #fff3cd;
  color: #333;
  padding: 1px 2px;
  border-radius: 2px;
}

.search-highlight-current {
  background-color: #ffeb3b !important; /* 更亮的黄色背景 */
  color: #000 !important;
  font-weight: bold;
  padding: 1px 3px;
  border-radius: 2px;
  box-shadow: 0 0 0 1px rgba(255, 193, 7, 0.5);
  position: relative;
}

/* MDB内容的双重高亮效果 */
.mdb-content-text .search-highlight-current {
  background-color: #ffeb3b !important;
  color: #000 !important;
  font-weight: bold;
  padding: 1px 3px;
  border-radius: 2px;
  box-shadow: 0 0 0 1px rgba(255, 193, 7, 0.5);
}

/* MDB文本行样式 */
.mdb-text-lines {
  font-family: 'Courier New', monospace;
  font-size: 12px;
  line-height: 1.5;
  color: #333;
  white-space: pre-wrap;
  word-wrap: break-word;
}

.mdb-text-line {
  padding: 2px 0;
  transition: all 0.2s ease;
  border-radius: 4px;
  margin: 1px 0;
}

/* MDB文本行的双重高亮效果 */
.mdb-text-line.search-highlight-current {
  background-color: rgba(255, 193, 7, 0.15); /* 浅黄色背景 - 整行高亮 */
  border: 2px solid #ff8c00; /* 橙色边框 */
  border-radius: 4px;
  padding: 4px 8px;
  margin: 2px 0;
  transition: all 0.2s ease;
}

.mdb-text-line.search-highlight-current span {
  font-weight: bold;
}

/* MDB文本行内部的高亮效果 */
.mdb-text-line.search-highlight-current span mark {
  background-color: #ffeb3b !important; /* 更亮的黄色背景 */
  color: #000 !important;
  font-weight: bold;
  padding: 1px 3px;
  border-radius: 2px;
  box-shadow: 0 0 0 1px rgba(255, 193, 7, 0.5);
}

/* CSR内容搜索高亮样式 - 双重高亮效果 */
.csr-text-line.search-highlight-current {
  background-color: rgba(255, 193, 7, 0.15); /* 浅黄色背景 - 整行高亮 */
  border: 2px solid #ff8c00; /* 橙色边框 */
  border-radius: 4px;
  padding: 4px 8px;
  margin: 2px 0;
  transition: all 0.2s ease;
}

.csr-text-line.search-highlight-current .csr-text {
  font-weight: bold;
}

/* 文本内部的高亮效果 */
.csr-text-line.search-highlight-current .csr-text mark {
  background-color: #ffeb3b !important; /* 更亮的黄色背景 */
  color: #000 !important;
  font-weight: bold;
  padding: 1px 3px;
  border-radius: 2px;
  box-shadow: 0 0 0 1px rgba(255, 193, 7, 0.5);
}

.mdb-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 200px;
  color: #666;
  font-size: 14px;
}

.mdb-error {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 200px;
}

/* 操作日志表格紧凑样式 */
.operation-log-table {
  :deep(.n-data-table) {
    .n-data-table-table {
      .n-data-table-tbody {
        .n-data-table-tr {
          height: 24px !important;
          .n-data-table-td {
            padding: 2px 8px !important;
            font-size: 12px;
            line-height: 1.2;
          }
        }
      }
      .n-data-table-thead {
        .n-data-table-tr {
          height: 28px !important;
          .n-data-table-th {
            padding: 4px 8px !important;
            font-size: 12px;
            line-height: 1.2;
          }
        }
      }
    }
  }
}

/* 操作日志统计面板已隐藏，但保留基础样式以支持功能逻辑 */
.operation-stats-panel {
  /* 统计面板已隐藏，但功能逻辑保留 */
}

/* 框架日志表格紧凑样式 */
.framework-log-table {
  :deep(.n-data-table) {
    .n-data-table-table {
      .n-data-table-tbody {
        .n-data-table-tr {
          height: 24px !important;
          .n-data-table-td {
            padding: 2px 8px !important;
            font-size: 12px;
            line-height: 1.2;
          }
        }
      }
      .n-data-table-thead {
        .n-data-table-tr {
          height: 28px !important;
          .n-data-table-th {
            padding: 4px 8px !important;
            font-size: 12px;
            line-height: 1.2;
          }
        }
      }
    }
  }
}

/* 框架日志统计面板紧凑样式 */
.framework-stats-panel {
  .stats-card {
    :deep(.n-card__content) {
      padding: 6px !important;
    }
  }
}

/* 更具体的框架日志表格样式，确保优先级 */
.log-content-area .component-content .framework-stats-panel .framework-log-table {
  :deep(.n-data-table) {
    .n-data-table-table {
      .n-data-table-tbody {
        .n-data-table-tr {
          height: 24px !important;
          .n-data-table-td {
            padding: 2px 8px !important;
            font-size: 12px;
            line-height: 1.2;
          }
        }
      }
      .n-data-table-thead {
        .n-data-table-tr {
          height: 28px !important;
          .n-data-table-th {
            padding: 4px 8px !important;
            font-size: 12px;
            line-height: 1.2;
          }
        }
      }
    }
  }
}

/* 更具体的框架日志统计面板样式 */
.log-content-area .component-content .framework-stats-panel .stats-card {
  :deep(.n-card__content) {
    padding: 6px !important;
  }
}

/* 时间分析抽屉样式 */
.time-analysis-content {
  padding: 16px 0;
}

.time-info-section,
.time-range-section {
  margin-bottom: 24px;
}

.time-info-section h3,
.time-range-section h3 {
  margin: 0 0 12px 0;
  font-size: 16px;
  font-weight: 600;
  color: var(--n-text-color);
}

.selected-time {
  padding: 12px;
  background-color: var(--n-color);
  border-radius: 6px;
  border: 1px solid var(--n-border-color);
}

.time-display {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}

.time-section {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.time-text {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  color: var(--n-text-color);
  cursor: pointer;
  padding: 8px 12px;
  border-radius: 6px;
  border: 2px dashed transparent;
  transition: all 0.2s ease;
  position: relative;
}

.time-text:hover {
  background-color: var(--n-color-hover);
  border-color: var(--n-border-color);
  transform: translateY(-1px);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.edit-hint {
  font-size: 12px;
  color: var(--n-text-color-3);
  opacity: 0;
  transition: opacity 0.2s ease;
}

.time-text:hover .edit-hint {
  opacity: 1;
}

.edit-icon {
  color: var(--n-text-color-3);
  opacity: 0.6;
  transition: all 0.2s ease;
  margin-left: 2px;
}

.time-text:hover .edit-icon {
  opacity: 1;
  color: var(--n-primary-color);
  transform: scale(1.1);
}

.time-edit {
  display: flex;
  align-items: center;
}

.time-navigation {
  display: flex;
  gap: 8px;
  flex-shrink: 0;
  align-items: center;
}

/* 时间范围选择器样式 */
.time-range-selectors {
  display: flex;
  gap: 16px;
  align-items: center;
  margin-right: 12px;
}

.range-selector {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
}

.range-selector label {
  font-size: 12px;
  color: var(--n-text-color-3);
  white-space: nowrap;
  font-weight: 500;
}

.time-range {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.time-item {
  display: flex;
  align-items: center;
  gap: 8px;
}

.time-item label {
  font-weight: 500;
  min-width: 80px;
  color: var(--n-text-color-2);
}

.actions-section {
  margin-top: 32px;
  padding-top: 16px;
  border-top: 1px solid var(--n-border-color);
}

.log-detail-section {
  margin-top: 24px;
}

.log-detail-content {
  border: 1px solid #f0f0f0;
  border-radius: 6px;
  padding: 16px;
  overflow: hidden;
}

.log-list {
  height: 100%;
  overflow-y: auto;
}

.log-detail-item {
  padding: 8px 0;
  border-bottom: 1px solid #f0f0f0;
  background-color: #ffffff;
}

.log-detail-item-even {
  background-color: #f8f9fa;
}

.log-detail-line {
  display: flex;
  align-items: flex-start;
  gap: 4px;
  flex-wrap: wrap;
}

.log-detail-line-number {
  min-width: 50px;
  flex-shrink: 0;
  font-weight: 600;
  color: #666;
  text-align: center;
  font-size: 12px;
}

.log-detail-time {
  min-width: 150px;
  flex-shrink: 0;
}

.log-detail-component {
  min-width: 120px;
  flex-shrink: 0;
}

.log-detail-level {
  min-width: 60px;
  flex-shrink: 0;
}

.log-detail-message {
  flex: 1;
  min-width: 0;
  word-wrap: break-word;
  word-break: break-all;
  white-space: normal;
  line-height: 1.4;
}

.no-logs {
  padding: 16px;
  text-align: center;
}

.time-info-section {
  margin-bottom: 24px;
}

.time-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 16px;
}

.time-header h3 {
  margin: 0;
  font-size: 16px;
  font-weight: 600;
  color: var(--n-text-color-1);
}

.time-format-switch {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 8px;
  background-color: var(--n-color-modal);
  border-radius: 6px;
  border: 1px solid var(--n-border-color);
}

.time-format-switch .n-text {
  font-size: 12px;
  white-space: nowrap;
}

.time-format-switch .n-button-group {
  flex-shrink: 0;
}

.time-format-switch .n-button {
  min-width: 40px;
  font-weight: 500;
  transition: all 0.2s ease;
}

.time-format-switch .n-button:hover {
  transform: translateY(-1px);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

/* 拖拽手柄样式 */
.drag-handle {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  cursor: grab;
  color: #999;
  border-radius: 4px;
  transition: all 0.2s ease;
  flex-shrink: 0;
  margin-right: 4px;
}

.drag-handle:hover {
  background-color: rgba(102, 126, 234, 0.1);
  color: #667eea;
  transform: scale(1.1);
}

.drag-handle:active {
  cursor: grabbing;
  transform: scale(0.95);
}

/* 拖拽相关样式 - 现在只用于拖拽手柄 */

/* 日志滚动条样式 - 大尺寸常驻显示 */
.log-content {
  overflow-y: auto !important;
  overflow-x: auto !important;
}

.log-content::-webkit-scrollbar {
  width: 24px !important;
  height: 24px !important;
  -webkit-appearance: none !important;
}

.log-content::-webkit-scrollbar-track {
  background: #f0f0f0 !important;
  border-radius: 12px !important;
  border: 2px solid #d8d8d8 !important;
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1) !important;
  cursor: pointer !important;
  transition: background-color 0.2s ease !important;
}

.log-content::-webkit-scrollbar-track:hover {
  background: #e0e0e0 !important;
  border-color: #c0c0c0 !important;
}

.log-content::-webkit-scrollbar-thumb {
  background: #c0c0c0 !important;
  border-radius: 12px !important;
  border: 3px solid #f0f0f0 !important;
  min-height: 100px !important;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2) !important;
  transition: all 0.3s ease !important;
}

.log-content::-webkit-scrollbar-thumb:hover {
  background: #a8a8a8 !important;
  border: 3px solid #e8e8e8 !important;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3) !important;
  transform: scale(1.05) !important;
}

.log-content::-webkit-scrollbar-thumb:active {
  background: #909090 !important;
  border: 3px solid #d8d8d8 !important;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4) !important;
  transform: scale(1.1) !important;
}

.log-content::-webkit-scrollbar-corner {
  background: #f0f0f0 !important;
  border-radius: 12px !important;
}

/* Firefox滚动条样式 */
.log-content {
  scrollbar-width: auto !important;
  scrollbar-color: #c0c0c0 #f0f0f0 !important;
}

/* 滚动条增强样式 - 为大数据量优化 */
.log-content .n-virtual-list::-webkit-scrollbar-thumb {
  /* 添加渐变效果，提供更好的视觉反馈 */
  background: linear-gradient(180deg, #d0d0d0 0%, #c0c0c0 50%, #b0b0b0 100%) !important;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.5), 
              0 2px 4px rgba(0, 0, 0, 0.1) !important;
}

.log-content .n-virtual-list::-webkit-scrollbar-thumb:hover {
  /* hover时的渐变效果 */
  background: linear-gradient(180deg, #b8b8b8 0%, #a8a8a8 50%, #989898 100%) !important;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.6), 
              0 4px 8px rgba(0, 0, 0, 0.2) !important;
}

.log-content .n-virtual-list::-webkit-scrollbar-thumb:active {
  /* 拖拽时的渐变效果 */
  background: linear-gradient(180deg, #a0a0a0 0%, #909090 50%, #808080 100%) !important;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.4), 
              0 6px 12px rgba(0, 0, 0, 0.3) !important;
}

/* 滚动条轨道增强 */
.log-content .n-virtual-list::-webkit-scrollbar-track {
  background: linear-gradient(180deg, #f8f8f8 0%, #f0f0f0 50%, #e8e8e8 100%) !important;
  box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.05) !important;
}

.log-content .n-virtual-list::-webkit-scrollbar-track:hover {
  background: linear-gradient(180deg, #f0f0f0 0%, #e8e8e8 50%, #e0e0e0 100%) !important;
}

/* 确保Naive UI虚拟列表的滚动条显示 */
.log-content .n-virtual-list {
  overflow: auto !important;
}

.log-content .n-virtual-list::-webkit-scrollbar {
  width: 28px !important; /* 增加滚动条宽度，提供更大的点击区域 */
  height: 28px !important;
}

.log-content .n-virtual-list::-webkit-scrollbar-track {
  background: #f0f0f0 !important;
  border-radius: 12px !important;
  border: 2px solid #d8d8d8 !important;
  cursor: pointer !important;
  transition: background-color 0.2s ease !important;
}

.log-content .n-virtual-list::-webkit-scrollbar-track:hover {
  background: #e0e0e0 !important;
  border-color: #c0c0c0 !important;
}

.log-content .n-virtual-list::-webkit-scrollbar-thumb {
  background: #c0c0c0 !important;
  border-radius: 12px !important;
  border: 3px solid #f0f0f0 !important;
  min-height: 120px !important; /* 增加最小高度，确保大数据量时滚动条不会太小 */
  transition: all 0.2s ease !important;
}

.log-content .n-virtual-list::-webkit-scrollbar-thumb:hover {
  background: #a8a8a8 !important;
  border: 3px solid #e8e8e8 !important;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3) !important;
  transform: scale(1.05) !important;
  min-height: 140px !important; /* hover时进一步增加高度 */
}

.log-content .n-virtual-list::-webkit-scrollbar-thumb:active {
  background: #909090 !important;
  border: 3px solid #d8d8d8 !important;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4) !important;
  transform: scale(1.1) !important;
  min-height: 150px !important; /* 拖拽时最大高度 */
}

.log-content:hover {
  scrollbar-color: #a8a8a8 #e8e8e8 !important;
}

/* 深色主题下的滚动条样式 */
@media (prefers-color-scheme: dark) {
  .log-content::-webkit-scrollbar-track {
    background: #2a2a2a !important;
    border-color: #404040 !important;
  }
  
  .log-content::-webkit-scrollbar-thumb {
    background: #606060 !important;
    border-color: #2a2a2a !important;
  }
  
  .log-content::-webkit-scrollbar-thumb:hover {
    background: #707070 !important;
    border-color: #404040 !important;
  }
  
  .log-content::-webkit-scrollbar-thumb:active {
    background: #808080 !important;
    border-color: #505050 !important;
  }
  
  .log-content::-webkit-scrollbar-corner {
    background: #2a2a2a !important;
  }
  
  .log-content {
    scrollbar-color: #606060 #2a2a2a !important;
  }
  
  .log-content:hover {
    scrollbar-color: #707070 #404040 !important;
  }
}

.resizable-area {
  position: relative;
  border: 1px solid transparent;
  transition: border-color 0.2s ease;
}

.resizable-area:hover {
  border-color: #e0e0e0;
}

.resizable-area.resizing {
  border-color: #1890ff;
  will-change: height;
  transform: translateZ(0);
  /* 性能优化：减少重绘和重排 */
  contain: layout style;
  backface-visibility: hidden;
}

.resize-handle {
  position: absolute;
  bottom: -6px; /* 向下扩展拖拽区域 */
  left: 0;
  right: 0;
  height: 20px; /* 增加拖拽区域高度，从8px增加到20px */
  cursor: row-resize;
  user-select: none;
  z-index: 10;
  background-color: transparent;
  transition: background-color 0.15s ease;
  /* 添加拖拽提示 */
  display: flex;
  align-items: center;
  justify-content: center;
  will-change: transform;
  transform: translateZ(0);
  /* 性能优化：减少重绘和重排 */
  contain: layout style;
  backface-visibility: hidden;
}

.resize-handle:hover {
  background-color: rgba(0, 0, 0, 0.05); /* 使用更柔和的灰色 */
}

.resize-handle.resizing {
  background-color: rgba(0, 0, 0, 0.08); /* 拖拽时的背景色 */
}

.resize-handle-line {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 60px; /* 增加线条宽度 */
  height: 3px; /* 减少线条高度，从4px减少到3px */
  background-color: #d9d9d9; /* 使用更柔和的灰色 */
  border-radius: 2px;
  transition: all 0.15s ease;
}

.resize-handle:hover .resize-handle-line {
  background-color: #bfbfbf; /* 悬停时的颜色 */
  width: 70px; /* 悬停时增加宽度，从80px减少到70px */
}

.resize-handle-dots {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: flex;
  gap: 5px; /* 减少点之间的间距，从6px减少到5px */
  align-items: center;
}

.resize-dot {
  width: 5px; /* 减少点的大小，从6px减少到5px */
  height: 5px;
  background-color: #d9d9d9; /* 使用更柔和的灰色 */
  border-radius: 50%;
  transition: all 0.15s ease;
}

.resize-handle:hover .resize-dot {
  background-color: #bfbfbf; /* 悬停时的颜色 */
  transform: scale(1.1); /* 悬停时放大，从1.2减少到1.1 */
}

.resize-handle.resizing .resize-dot {
  background-color: #8c8c8c; /* 拖拽时的颜色 */
  transform: scale(1.15); /* 拖拽时进一步放大，从1.3减少到1.15 */
}

/* 新增日志板块高亮效果 */
.newly-added-highlight {
  animation: newPanelHighlight 2s ease-in-out;
  border: 2px solid #18a058 !important;
  box-shadow: 0 0 20px rgba(24, 160, 88, 0.3) !important;
}

@keyframes newPanelHighlight {
  0% {
    border-color: #18a058;
    box-shadow: 0 0 20px rgba(24, 160, 88, 0.5);
    transform: scale(1.02);
  }
  50% {
    border-color: #36ad6a;
    box-shadow: 0 0 25px rgba(24, 160, 88, 0.4);
    transform: scale(1.01);
  }
  100% {
    border-color: #18a058;
    box-shadow: 0 0 20px rgba(24, 160, 88, 0.3);
    transform: scale(1);
  }
}

/* 组件MDB按钮样式 */
.component-mdb-btn {
  margin-right: 0;
  flex-shrink: 0;
}

/* 时间分析页面拖拽手柄样式 */
.time-analysis-resize-handle {
  position: relative;
  height: 12px;
  margin: 8px 0;
  cursor: row-resize;
  user-select: none;
  background-color: transparent;
  transition: background-color 0.15s ease;
  display: flex;
  align-items: center;
  justify-content: center;
}

.time-analysis-resize-handle:hover {
  background-color: rgba(0, 0, 0, 0.05);
}

.time-analysis-resize-handle .resize-handle-line {
  width: 40px;
  height: 2px;
  background-color: #d9d9d9;
  border-radius: 1px;
  transition: all 0.15s ease;
}

.time-analysis-resize-handle:hover .resize-handle-line {
  background-color: #bfbfbf;
  width: 50px;
}

.time-analysis-resize-handle .resize-handle-dots {
  gap: 3px;
}

.time-analysis-resize-handle .resize-dot {
  width: 3px;
  height: 3px;
  background-color: #d9d9d9;
  border-radius: 50%;
  transition: all 0.15s ease;
}

.time-analysis-resize-handle:hover .resize-dot {
  background-color: #bfbfbf;
  transform: scale(1.1);
}

/* MDB按钮和复选框之间的分隔线 */
.mdb-separator {
  width: 1px;
  height: 20px;
  background: linear-gradient(180deg, rgba(0, 0, 0, 0.08), rgba(0, 0, 0, 0.04));
  margin: 0 12px;
  flex-shrink: 0;
}

.component-checkbox-item {
  display: flex;
  align-items: center;
  margin-bottom: 6px;
  padding: 4px 8px;
  border-radius: 6px;
  transition: background-color 0.2s ease;
}

.component-checkbox-item:hover {
  background-color: rgba(24, 144, 255, 0.05);
}

.component-checkbox-item .component-checkbox {
  flex: 1;
  min-width: 0;
  margin-left: 4px;
}

/* BMC版本抽屉样式 */
.bmc-version-drawer-content {
  padding: 16px;
  height: 100%;
  display: flex;
  flex-direction: column;
  position: relative;
}

.bmc-version-content {
  margin-top: 16px;
}

.bmc-version-content-text {
  background-color: #f8f9fa;
  border: 1px solid #e9ecef;
  border-radius: 6px;
  padding: 16px;
  height: auto;
  min-height: 400px;
  overflow-y: auto;
}

.bmc-version-content-text pre {
  margin: 0;
  white-space: pre-wrap;
  word-wrap: break-word;
  font-family: 'Courier New', monospace;
  font-size: 14px;
  line-height: 1.5;
  color: #333;
}

.bmc-version-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 40px 20px;
  color: #666;
}

.bmc-version-error {
  padding: 40px 20px;
  text-align: center;
}

/* APP版本抽屉样式 */
.app-version-drawer-content {
  padding: 16px;
  height: 100%;
  display: flex;
  flex-direction: column;
  position: relative;
}

/* APP版本搜索容器样式 */
.app-version-search-container {
  position: sticky;
  top: 0;
  z-index: 10;
  background-color: white;
  padding: 16px;
  border-bottom: 1px solid #f0f0f0;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  margin: -16px -16px 16px -16px;
}

/* APP版本可滚动内容区域 */
.app-version-scrollable-content {
  flex: 1;
  overflow-y: auto;
  padding: 16px;
  margin: -16px;
}

/* 确保APP版本抽屉内容容器有正确的定位上下文 */
:deep(.n-drawer-content) {
  position: relative;
}

.app-version-content {
  margin-top: 16px;
}

.app-version-content-text {
  background-color: #f8f9fa;
  border: 1px solid #e9ecef;
  border-radius: 6px;
  padding: 16px;
  height: auto;
  min-height: 400px;
  overflow-y: auto;
}

.app-version-content-text pre {
  margin: 0;
  white-space: pre-wrap;
  word-wrap: break-word;
  font-family: 'Courier New', monospace;
  font-size: 14px;
  line-height: 1.5;
  color: #333;
}

.app-version-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 40px 20px;
  color: #666;
}

.app-version-error {
  padding: 40px 20px;
  text-align: center;
}

/* APP版本搜索样式 */
.app-version-search-bar {
  margin-bottom: 8px;
}

.app-version-search-nav {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px 16px;
  background-color: #f0f0f0;
  border-radius: 6px;
  margin-bottom: 16px;
}

.app-version-search-highlight {
  background-color: #fff3cd;
  color: #333;
  padding: 1px 2px;
  border-radius: 2px;
}

.app-version-search-highlight-current {
  background-color: #ffeb3b !important; /* 更亮的黄色背景 */
  color: #000 !important;
  font-weight: bold;
  padding: 1px 3px;
  border-radius: 2px;
  box-shadow: 0 0 0 1px rgba(255, 193, 7, 0.5);
  position: relative;
}

/* APP版本内容的双重高亮效果 */
.app-version-content-text .app-version-search-highlight-current {
  background-color: #ffeb3b !important;
  color: #000 !important;
  font-weight: bold;
  padding: 1px 3px;
  border-radius: 2px;
  box-shadow: 0 0 0 1px rgba(255, 193, 7, 0.5);
}

/* APP版本文本行样式 */
.app-version-text-lines {
  font-family: 'Courier New', monospace;
  font-size: 12px;
  line-height: 1.5;
  color: #333;
  white-space: pre-wrap;
  word-wrap: break-word;
}

.app-version-text-line {
  padding: 2px 0;
  transition: all 0.2s ease;
  border-radius: 4px;
  margin: 1px 0;
}

/* APP版本文本行的双重高亮效果 */
.app-version-text-line.search-highlight-current {
  background-color: rgba(255, 193, 7, 0.15); /* 浅黄色背景 - 整行高亮 */
  border: 2px solid #ff8c00; /* 橙色边框 */
  border-radius: 4px;
  padding: 4px 8px;
  margin: 2px 0;
  transition: all 0.2s ease;
}

.app-version-text-line.search-highlight-current span {
  font-weight: bold;
}

/* APP版本文本行内部的高亮效果 */
.app-version-text-line.search-highlight-current span mark {
  background-color: #ffeb3b !important; /* 更亮的黄色背景 */
  color: #000 !important;
  font-weight: bold;
  padding: 1px 3px;
  border-radius: 2px;
  box-shadow: 0 0 0 1px rgba(255, 193, 7, 0.5);
}
/* 页面提示样式 */
.page-notification {
  position: fixed;
  top: 20px;
  right: 20px;
  padding: 12px 20px;
  border-radius: 6px;
  font-size: 14px;
  font-weight: 500;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  z-index: 9999;
  animation: slideInRight 0.3s ease-out;
}

.page-notification.info {
  background-color: #e6f7ff;
  border: 1px solid #91d5ff;
  color: #1890ff;
}

.page-notification.warning {
  background-color: #fff7e6;
  border: 1px solid #ffd591;
  color: #fa8c16;
}

.page-notification.success {
  background-color: #f6ffed;
  border: 1px solid #b7eb8f;
  color: #52c41a;
}

.page-notification.error {
  background-color: #fff2f0;
  border: 1px solid #ffccc7;
  color: #ff4d4f;
}

@keyframes slideInRight {
  from {
    transform: translateX(100%);
    opacity: 0;
  }
  to {
    transform: translateX(0);
    opacity: 1;
  }
}

/* 区域筛选状态指示样式 */
.stats-panel.has-filtered-data {
  background-color: rgba(144, 238, 144, 0.3) !important; /* 淡绿色 */
  border: 2px solid rgba(144, 238, 144, 0.6) !important;
  border-radius: 8px !important;
  transition: all 0.3s ease !important;
}

.stats-panel.has-filtered-data .stats-card {
  background-color: rgba(144, 238, 144, 0.1) !important;
}

.stats-panel.no-filtered-data {
  border-color: rgba(156, 163, 175, 0.2) !important;
  background: linear-gradient(135deg, rgba(249, 250, 251, 0.8) 0%, rgba(243, 244, 246, 0.9) 100%) !important;
  box-shadow: 0 1px 4px rgba(156, 163, 175, 0.1) !important;
  transition: all 0.3s ease !important;
}

.stats-panel.no-filtered-data .stats-card {
  background-color: rgba(249, 250, 251, 0.5) !important;
}

/* 左侧日志类型区域筛选状态指示样式 */
.log-type-section.has-filtered-data {
  background-color: rgba(144, 238, 144, 0.3) !important; /* 淡绿色 */
  border: 2px solid rgba(144, 238, 144, 0.6) !important;
  border-radius: 8px !important;
  padding: 8px !important;
  margin: 4px 0 !important;
  transition: all 0.3s ease !important;
}

.log-type-section.no-filtered-data {
  border-color: rgba(156, 163, 175, 0.2) !important;
  background: linear-gradient(135deg, rgba(249, 250, 251, 0.8) 0%, rgba(243, 244, 246, 0.9) 100%) !important;
  box-shadow: 0 1px 4px rgba(156, 163, 175, 0.1) !important;
  transition: all 0.3s ease !important;
}

.log-type-section.no-filtered-data .log-type-title {
  color: #9ca3af !important;
}

.log-type-hint {
  font-size: 12px;
  color: #9ca3af;
  margin-top: 4px;
  margin-left: 20px;
  font-style: italic;
  opacity: 0.8;
}

/* 组件级别的筛选状态指示样式 */
.component-item-wrapper.has-filtered-data {
  background-color: rgba(144, 238, 144, 0.3) !important; /* 淡绿色 */
  border: 2px solid rgba(144, 238, 144, 0.6) !important;
  border-radius: 8px !important;
  padding: 4px !important;
  margin: 2px 0 !important;
  transition: all 0.3s ease !important;
}

/* 没有匹配数据的组件置灰样式 - 使用和 component-no-logs 相同的样式 */
.component-item-wrapper.no-filtered-data {
  border-color: rgba(156, 163, 175, 0.2) !important;
  background: linear-gradient(135deg, rgba(249, 250, 251, 0.8) 0%, rgba(243, 244, 246, 0.9) 100%) !important;
  box-shadow: 0 1px 4px rgba(156, 163, 175, 0.1) !important;
  transition: all 0.3s ease !important;
}

.component-item-wrapper.no-filtered-data .n-checkbox__label {
  color: #9ca3af !important;
}

.component-item-wrapper.no-filtered-data .n-checkbox {
  opacity: 0.6 !important;
}

/* MDB 按钮在置灰状态下保持原有颜色 */
.component-item-wrapper.no-filtered-data .mdb-btn {
  opacity: 1 !important;
  filter: none !important;
}

.component-item-wrapper.no-filtered-data .mdb-btn .mdb-text {
  opacity: 1 !important;
  color: #3b82f6 !important;
}

/* 告警日志表格样式 */
.alert-log-content {
  width: 100%;
  height: calc(100% - 60px); /* 减去搜索栏高度 */
  overflow: hidden;
  position: relative;
}

.alert-log-table-wrapper {
  width: 100%;
  height: 100%;
  overflow: auto;
  position: relative;
}

.alert-log-table {
  width: 100%;
  max-width: 100%;
  position: relative;
}

.alert-log-table .n-data-table {
  width: 100%;
  height: 100%;
}

.alert-log-table .n-data-table-base-table {
  position: relative;
}

.alert-log-table .n-data-table-thead {
  position: sticky;
  top: 0;
  z-index: 100;
  background: white;
}

/* 告警日志表格列分割边界 */
.alert-log-table .n-data-table-th {
  border-right: 2px solid var(--n-border-color);
  position: sticky !important;
  top: 0 !important;
  z-index: 1000 !important;
  background: linear-gradient(135deg, #4f46e5 0%, #3b82f6 50%, #2563eb 100%) !important;
  border-bottom: 4px solid #1d4ed8 !important;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25), 0 2px 6px rgba(59, 130, 246, 0.3) !important;
  font-weight: 800 !important;
  font-size: 14px !important;
  color: white !important;
  text-transform: uppercase !important;
  letter-spacing: 1px !important;
  backdrop-filter: blur(10px) !important;
}

.alert-log-table .n-data-table-td {
  border-right: 1px solid var(--n-border-color);
}

/* 最后一列不需要右边框 */
.alert-log-table .n-data-table-th:last-child,
.alert-log-table .n-data-table-td:last-child {
  border-right: none;
}

/* 增强列头分割效果 */
.alert-log-table .n-data-table-th::after {
  content: '';
  position: absolute;
  right: -1px;
  top: 10%;
  bottom: 10%;
  width: 2px;
  background: linear-gradient(to bottom, transparent, var(--n-border-color), transparent);
  opacity: 0.6;
}

.alert-log-table .n-data-table-th:last-child::after {
  display: none;
}

/* 筛选按钮样式优化 */
.alert-log-table .n-data-table-filter {
  margin-left: 12px;
  padding: 4px 8px !important;
  border-radius: 6px !important;
  background-color: rgba(255, 255, 255, 0.9) !important;
  border: 2px solid rgba(255, 255, 255, 0.8) !important;
  color: #3b82f6 !important;
  font-weight: 600 !important;
  opacity: 0.9;
  transition: all 0.3s ease !important;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2) !important;
}

.alert-log-table .n-data-table-filter:hover {
  opacity: 1 !important;
  background-color: white !important;
  border-color: white !important;
  transform: translateY(-2px) !important;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3) !important;
  color: #1d4ed8 !important;
}

/* 确保筛选按钮与列标题有明确的分隔 */
.alert-log-table .n-data-table-th-content {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 18px !important;
  min-height: 52px !important;
  background: inherit !important;
  border-radius: 0;
}

/* 列标题文本样式 */
.alert-log-table .n-data-table-th-content .n-data-table-th__title {
  font-weight: 800 !important;
  color: white !important;
  flex: 1;
  text-align: left;
  text-transform: uppercase !important;
  letter-spacing: 1px !important;
  font-size: 13px !important;
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5) !important;
}

/* 筛选按钮容器 */
.alert-log-table .n-data-table-th-content .n-data-table-filter-wrapper {
  display: flex;
  align-items: center;
  flex-shrink: 0;
}

/* 排除关键词输入框样式 */
.exclude-search-container {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-left: 12px;
  min-width: 0;
  flex: 1;
}

.exclude-input-wrapper {
  position: relative;
}

.exclude-search-input {
  min-width: 300px;
  max-width: 500px;
  width: auto;
  flex: 1;
}

/* 内联标签容器 */
.exclude-tags-inline {
  display: flex;
  align-items: center;
  gap: 4px;
  max-width: none;
  overflow: visible;
  flex-wrap: wrap;
  min-width: 0;
}

/* 内联标签样式 */
.exclude-tag-inline {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 1px 4px;
  background-color: #dbeafe;
  color: #1d4ed8;
  border: 1px solid #bfdbfe;
  border-radius: 8px;
  font-size: 10px;
  font-weight: 500;
  transition: all 0.2s ease;
  white-space: nowrap;
  flex-shrink: 0;
  position: relative;
  padding-right: 8px;
  margin: 1px 0;
}

.exclude-tag-inline:hover {
  background-color: #bfdbfe;
  border-color: #93c5fd;
}

/* 内联标签文本 */
.exclude-tag-text {
  max-width: 40px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 内联删除按钮 - 轻量×符号 */
.exclude-tag-remove-x {
  position: absolute;
  top: -2px;
  right: -2px;
  width: 12px;
  height: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: #ef4444;
  color: white;
  border-radius: 50%;
  font-size: 8px;
  font-weight: bold;
  cursor: pointer;
  transition: all 0.2s ease;
  line-height: 1;
  z-index: 1;
}

.exclude-tag-remove-x:hover {
  background-color: #dc2626;
  transform: scale(1.1);
}

.exclude-tag-remove-x:active {
  transform: scale(0.9);
}







.exclude-keywords-display {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  min-height: 24px;
  align-items: flex-start;
}

.exclude-keyword-tag {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 8px;
  background-color: #dbeafe;
  color: #1d4ed8;
  border: 1px solid #bfdbfe;
  border-radius: 12px;
  font-size: 12px;
  font-weight: 500;
  transition: all 0.2s ease;
}

.exclude-keyword-tag:hover {
  background-color: #bfdbfe;
  border-color: #93c5fd;
}

.exclude-keyword-tag .remove-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background-color: #dc2626;
  color: white;
  font-size: 10px;
  font-weight: bold;
  cursor: pointer;
  transition: all 0.2s ease;
  border: none;
  padding: 0;
  line-height: 1;
}

.exclude-keyword-tag .remove-btn:hover {
  background-color: #b91c1c;
  transform: scale(1.1);
}

.exclude-keyword-tag .remove-btn:active {
  transform: scale(0.95);
}

/* 排除关键词标签的文本部分 */
.exclude-keyword-tag .keyword-text {
  max-width: 120px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 空状态提示 */
.exclude-keywords-display:empty::before {
  content: "暂无排除关键词";
  color: #9ca3af;
  font-size: 12px;
  font-style: italic;
}

/* 响应式设计 */
@media (max-width: 768px) {
  .exclude-search-input {
    min-width: 150px;
  }
  
  .exclude-keyword-tag .keyword-text {
    max-width: 80px;
  }
}
</style>

<!-- 全局样式，用于告警日志表格 -->
<style>
/* 告警日志表格全局样式 - 确保能够覆盖Naive UI默认样式 */
.alert-log-table-wrapper {
  height: 100% !important;
  overflow-y: auto !important;
  position: relative !important;
}

.alert-log-table .n-data-table {
  position: relative !important;
}

.alert-log-table .n-data-table-base-table {
  position: relative !important;
}

.alert-log-table .n-data-table-thead {
  position: sticky !important;
  top: 0 !important;
  z-index: 100 !important;
  background: #f8fafc !important;
}

.alert-log-table .n-data-table-th {
  position: sticky !important;
  top: 0 !important;
  z-index: 100 !important;
  background: #f8fafc !important;
  border-bottom: 2px solid #e2e8f0 !important;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08) !important;
  font-weight: 600 !important;
  font-size: 13px !important;
  color: #475569 !important;
  border-right: 1px solid #e2e8f0 !important;
}

.alert-log-table .n-data-table-th-content {
  padding: 12px 16px !important;
  min-height: 48px !important;
  background: inherit !important;
}

.alert-log-table .n-data-table-th-content .n-data-table-th__title {
  font-weight: 600 !important;
  color: #475569 !important;
  font-size: 13px !important;
}

.alert-log-table .n-data-table-filter {
  padding: 4px 8px !important;
  border-radius: 4px !important;
  background-color: #f1f5f9 !important;
  border: 1px solid #cbd5e1 !important;
  color: #64748b !important;
  font-weight: 500 !important;
  transition: all 0.2s ease !important;
}

.alert-log-table .n-data-table-filter:hover {
  background-color: #e2e8f0 !important;
  border-color: #94a3b8 !important;
  color: #475569 !important;
}
</style>